Chris@1115
|
1 # Redmine - project management software
|
Chris@1295
|
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
|
Chris@1115
|
3 #
|
Chris@1115
|
4 # This program is free software; you can redistribute it and/or
|
Chris@1115
|
5 # modify it under the terms of the GNU General Public License
|
Chris@1115
|
6 # as published by the Free Software Foundation; either version 2
|
Chris@1115
|
7 # of the License, or (at your option) any later version.
|
Chris@1115
|
8 #
|
Chris@1115
|
9 # This program is distributed in the hope that it will be useful,
|
Chris@1115
|
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
|
Chris@1115
|
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
Chris@1115
|
12 # GNU General Public License for more details.
|
Chris@1115
|
13 #
|
Chris@1115
|
14 # You should have received a copy of the GNU General Public License
|
Chris@1115
|
15 # along with this program; if not, write to the Free Software
|
Chris@1115
|
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
Chris@1115
|
17
|
Chris@0
|
18 module Redmine
|
Chris@0
|
19 module I18n
|
Chris@0
|
20 def self.included(base)
|
Chris@0
|
21 base.extend Redmine::I18n
|
Chris@0
|
22 end
|
Chris@909
|
23
|
Chris@0
|
24 def l(*args)
|
Chris@0
|
25 case args.size
|
Chris@0
|
26 when 1
|
Chris@0
|
27 ::I18n.t(*args)
|
Chris@0
|
28 when 2
|
Chris@0
|
29 if args.last.is_a?(Hash)
|
Chris@0
|
30 ::I18n.t(*args)
|
Chris@0
|
31 elsif args.last.is_a?(String)
|
Chris@0
|
32 ::I18n.t(args.first, :value => args.last)
|
Chris@0
|
33 else
|
Chris@0
|
34 ::I18n.t(args.first, :count => args.last)
|
Chris@0
|
35 end
|
Chris@0
|
36 else
|
Chris@0
|
37 raise "Translation string with multiple values: #{args.first}"
|
Chris@0
|
38 end
|
Chris@0
|
39 end
|
Chris@0
|
40
|
Chris@0
|
41 def l_or_humanize(s, options={})
|
Chris@0
|
42 k = "#{options[:prefix]}#{s}".to_sym
|
Chris@0
|
43 ::I18n.t(k, :default => s.to_s.humanize)
|
Chris@0
|
44 end
|
Chris@909
|
45
|
Chris@0
|
46 def l_hours(hours)
|
Chris@0
|
47 hours = hours.to_f
|
Chris@0
|
48 l((hours < 2.0 ? :label_f_hour : :label_f_hour_plural), :value => ("%.2f" % hours.to_f))
|
Chris@0
|
49 end
|
Chris@909
|
50
|
Chris@0
|
51 def ll(lang, str, value=nil)
|
Chris@0
|
52 ::I18n.t(str.to_s, :value => value, :locale => lang.to_s.gsub(%r{(.+)\-(.+)$}) { "#{$1}-#{$2.upcase}" })
|
Chris@0
|
53 end
|
Chris@0
|
54
|
Chris@0
|
55 def format_date(date)
|
Chris@0
|
56 return nil unless date
|
Chris@1115
|
57 options = {}
|
Chris@1115
|
58 options[:format] = Setting.date_format unless Setting.date_format.blank?
|
Chris@1115
|
59 options[:locale] = User.current.language unless User.current.language.blank?
|
Chris@1115
|
60 ::I18n.l(date.to_date, options)
|
Chris@0
|
61 end
|
Chris@909
|
62
|
Chris@0
|
63 def format_time(time, include_date = true)
|
Chris@0
|
64 return nil unless time
|
Chris@1115
|
65 options = {}
|
Chris@1115
|
66 options[:format] = (Setting.time_format.blank? ? :time : Setting.time_format)
|
Chris@1115
|
67 options[:locale] = User.current.language unless User.current.language.blank?
|
Chris@0
|
68 time = time.to_time if time.is_a?(String)
|
Chris@0
|
69 zone = User.current.time_zone
|
Chris@0
|
70 local = zone ? time.in_time_zone(zone) : (time.utc? ? time.localtime : time)
|
Chris@1115
|
71 (include_date ? "#{format_date(local)} " : "") + ::I18n.l(local, options)
|
Chris@0
|
72 end
|
Chris@0
|
73
|
Chris@0
|
74 def day_name(day)
|
Chris@0
|
75 ::I18n.t('date.day_names')[day % 7]
|
Chris@0
|
76 end
|
Chris@909
|
77
|
Chris@1115
|
78 def day_letter(day)
|
Chris@1115
|
79 ::I18n.t('date.abbr_day_names')[day % 7].first
|
Chris@1115
|
80 end
|
Chris@1115
|
81
|
Chris@0
|
82 def month_name(month)
|
Chris@0
|
83 ::I18n.t('date.month_names')[month]
|
Chris@0
|
84 end
|
Chris@909
|
85
|
Chris@0
|
86 def valid_languages
|
Chris@1115
|
87 ::I18n.available_locales
|
Chris@1115
|
88 end
|
Chris@1115
|
89
|
Chris@1115
|
90 # Returns an array of languages names and code sorted by names, example:
|
Chris@1115
|
91 # [["Deutsch", "de"], ["English", "en"] ...]
|
Chris@1115
|
92 #
|
Chris@1115
|
93 # The result is cached to prevent from loading all translations files.
|
Chris@1115
|
94 def languages_options
|
Chris@1115
|
95 ActionController::Base.cache_store.fetch "i18n/languages_options" do
|
Chris@1115
|
96 valid_languages.map {|lang| [ll(lang.to_s, :general_lang_name), lang.to_s]}.sort {|x,y| x.first <=> y.first }
|
Chris@1115
|
97 end
|
Chris@0
|
98 end
|
Chris@909
|
99
|
Chris@0
|
100 def find_language(lang)
|
Chris@0
|
101 @@languages_lookup = valid_languages.inject({}) {|k, v| k[v.to_s.downcase] = v; k }
|
Chris@0
|
102 @@languages_lookup[lang.to_s.downcase]
|
Chris@0
|
103 end
|
Chris@909
|
104
|
Chris@0
|
105 def set_language_if_valid(lang)
|
Chris@0
|
106 if l = find_language(lang)
|
Chris@0
|
107 ::I18n.locale = l
|
Chris@0
|
108 end
|
Chris@0
|
109 end
|
Chris@909
|
110
|
Chris@0
|
111 def current_language
|
Chris@0
|
112 ::I18n.locale
|
Chris@0
|
113 end
|
Chris@1115
|
114
|
Chris@1115
|
115 # Custom backend based on I18n::Backend::Simple with the following changes:
|
Chris@1115
|
116 # * lazy loading of translation files
|
Chris@1115
|
117 # * available_locales are determined by looking at translation file names
|
Chris@1115
|
118 class Backend
|
Chris@1115
|
119 (class << self; self; end).class_eval { public :include }
|
Chris@1115
|
120
|
Chris@1115
|
121 module Implementation
|
Chris@1115
|
122 include ::I18n::Backend::Base
|
Chris@1115
|
123
|
Chris@1115
|
124 # Stores translations for the given locale in memory.
|
Chris@1115
|
125 # This uses a deep merge for the translations hash, so existing
|
Chris@1115
|
126 # translations will be overwritten by new ones only at the deepest
|
Chris@1115
|
127 # level of the hash.
|
Chris@1115
|
128 def store_translations(locale, data, options = {})
|
Chris@1115
|
129 locale = locale.to_sym
|
Chris@1115
|
130 translations[locale] ||= {}
|
Chris@1115
|
131 data = data.deep_symbolize_keys
|
Chris@1115
|
132 translations[locale].deep_merge!(data)
|
Chris@1115
|
133 end
|
Chris@1115
|
134
|
Chris@1115
|
135 # Get available locales from the translations filenames
|
Chris@1115
|
136 def available_locales
|
Chris@1115
|
137 @available_locales ||= ::I18n.load_path.map {|path| File.basename(path, '.*')}.uniq.sort.map(&:to_sym)
|
Chris@1115
|
138 end
|
Chris@1115
|
139
|
Chris@1115
|
140 # Clean up translations
|
Chris@1115
|
141 def reload!
|
Chris@1115
|
142 @translations = nil
|
Chris@1115
|
143 @available_locales = nil
|
Chris@1115
|
144 super
|
Chris@1115
|
145 end
|
Chris@1115
|
146
|
Chris@1115
|
147 protected
|
Chris@1115
|
148
|
Chris@1115
|
149 def init_translations(locale)
|
Chris@1115
|
150 locale = locale.to_s
|
Chris@1115
|
151 paths = ::I18n.load_path.select {|path| File.basename(path, '.*') == locale}
|
Chris@1115
|
152 load_translations(paths)
|
Chris@1115
|
153 translations[locale] ||= {}
|
Chris@1115
|
154 end
|
Chris@1115
|
155
|
Chris@1115
|
156 def translations
|
Chris@1115
|
157 @translations ||= {}
|
Chris@1115
|
158 end
|
Chris@1115
|
159
|
Chris@1115
|
160 # Looks up a translation from the translations hash. Returns nil if
|
Chris@1115
|
161 # eiher key is nil, or locale, scope or key do not exist as a key in the
|
Chris@1115
|
162 # nested translations hash. Splits keys or scopes containing dots
|
Chris@1115
|
163 # into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as
|
Chris@1115
|
164 # <tt>%w(currency format)</tt>.
|
Chris@1115
|
165 def lookup(locale, key, scope = [], options = {})
|
Chris@1115
|
166 init_translations(locale) unless translations.key?(locale)
|
Chris@1115
|
167 keys = ::I18n.normalize_keys(locale, key, scope, options[:separator])
|
Chris@1115
|
168
|
Chris@1115
|
169 keys.inject(translations) do |result, _key|
|
Chris@1115
|
170 _key = _key.to_sym
|
Chris@1115
|
171 return nil unless result.is_a?(Hash) && result.has_key?(_key)
|
Chris@1115
|
172 result = result[_key]
|
Chris@1115
|
173 result = resolve(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol)
|
Chris@1115
|
174 result
|
Chris@1115
|
175 end
|
Chris@1115
|
176 end
|
Chris@1115
|
177 end
|
Chris@1115
|
178
|
Chris@1115
|
179 include Implementation
|
Chris@1115
|
180 # Adds fallback to default locale for untranslated strings
|
Chris@1115
|
181 include ::I18n::Backend::Fallbacks
|
Chris@1115
|
182 end
|
Chris@0
|
183 end
|
Chris@0
|
184 end
|