translation.rb 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. # Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
  2. class Translation < ApplicationModel
  3. include Translation::SynchronizesFromPo
  4. before_create :set_initial
  5. =begin
  6. reset translations to origin
  7. Translation.reset(locale)
  8. =end
  9. def self.reset(locale)
  10. # only push changed translations
  11. translations = Translation.where(locale: locale)
  12. translations.each do |translation|
  13. if translation.target_initial.blank?
  14. translation.destroy
  15. elsif translation.target != translation.target_initial
  16. translation.target = translation.target_initial
  17. translation.save
  18. end
  19. end
  20. true
  21. end
  22. =begin
  23. get list of translations
  24. list = Translation.lang('de-de')
  25. =end
  26. def self.lang(locale, admin = false)
  27. # use cache if not admin page is requested
  28. if !admin
  29. data = lang_cache_get(locale)
  30. return data if data
  31. end
  32. # show total translations as reference count
  33. data = {
  34. 'total' => Translation.where(locale: 'de-de').count,
  35. }
  36. list = []
  37. translations = if admin
  38. Translation.where(locale: locale.downcase).order(:source)
  39. else
  40. Translation.where(locale: locale.downcase).where.not(target: '').order(:source)
  41. end
  42. translations.each do |item|
  43. translation_item = if admin
  44. [
  45. item.id,
  46. item.source,
  47. item.target,
  48. item.target_initial,
  49. ]
  50. else
  51. [
  52. item.id,
  53. item.source,
  54. item.target,
  55. ]
  56. end
  57. list.push translation_item
  58. end
  59. # add presorted on top
  60. presorted_list = []
  61. %w[yes no or Year Years Month Months Day Days Hour Hours Minute Minutes Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec January February March April May June July August September October November December Mon Tue Wed Thu Fri Sat Sun Monday Tuesday Wednesday Thursday Friday Saturday Sunday].each do |presort|
  62. list.each do |item|
  63. next if item[1] != presort
  64. presorted_list.push item
  65. list.delete item
  66. # list.unshift presort
  67. end
  68. end
  69. data['list'] = presorted_list.concat list
  70. # set cache
  71. if !admin
  72. lang_cache_set(locale, data)
  73. end
  74. data
  75. end
  76. =begin
  77. translate strings in Ruby context, e. g. for notifications
  78. translated = Translation.translate('de-de', 'New')
  79. =end
  80. def self.translate(locale, string)
  81. find_source(locale, string)&.target || string
  82. end
  83. =begin
  84. find a translation record for a given locale and source string. 'find_by' might not be enough,
  85. because it could produce wrong matches on case insensitive MySQL databases.
  86. =end
  87. def self.find_source(locale, string)
  88. # MySQL might find the wrong record with find_by with case insensitive locales, so use a direct comparison.
  89. where(locale: locale, source: string).find { |t| t.source.eql? string }
  90. end
  91. =begin
  92. translate timestampes in ruby context, e. g. for notifications
  93. translated = Translation.timestamp('de-de', 'Europe/Berlin', '2018-10-10T10:00:00Z0')
  94. or
  95. translated = Translation.timestamp('de-de', 'Europe/Berlin', Time.zone.parse('2018-10-10T10:00:00Z0'))
  96. =end
  97. def self.timestamp(locale, timezone, timestamp)
  98. if timestamp.instance_of?(String)
  99. begin
  100. timestamp_parsed = Time.zone.parse(timestamp)
  101. return timestamp.to_s if !timestamp_parsed
  102. timestamp = timestamp_parsed
  103. rescue
  104. return timestamp.to_s
  105. end
  106. end
  107. begin
  108. timestamp = timestamp.in_time_zone(timezone)
  109. rescue
  110. return timestamp.to_s
  111. end
  112. record = Translation.where(locale: locale, source: 'FORMAT_DATETIME').pick(:target)
  113. return timestamp.to_s if !record
  114. record.sub!('dd', format('%<day>02d', day: timestamp.day))
  115. record.sub!('d', timestamp.day.to_s)
  116. record.sub!('mm', format('%<month>02d', month: timestamp.month))
  117. record.sub!('m', timestamp.month.to_s)
  118. record.sub!('yyyy', timestamp.year.to_s)
  119. record.sub!('yy', timestamp.year.to_s.last(2))
  120. record.sub!('SS', format('%<second>02d', second: timestamp.sec.to_s))
  121. record.sub!('MM', format('%<minute>02d', minute: timestamp.min.to_s))
  122. record.sub!('HH', format('%<hour>02d', hour: timestamp.hour.to_s))
  123. record.sub!('l', timestamp.strftime('%l'))
  124. record.sub!('P', timestamp.strftime('%P'))
  125. "#{record} (#{timezone})"
  126. end
  127. =begin
  128. translate date in ruby context, e. g. for notifications
  129. translated = Translation.date('de-de', '2018-10-10')
  130. or
  131. translated = Translation.date('de-de', Date.parse('2018-10-10'))
  132. =end
  133. def self.date(locale, date)
  134. if date.instance_of?(String)
  135. begin
  136. date_parsed = Date.parse(date)
  137. return date.to_s if !date_parsed
  138. date = date_parsed
  139. rescue
  140. return date.to_s
  141. end
  142. end
  143. return date.to_s if date.class != Date
  144. record = Translation.where(locale: locale, source: 'FORMAT_DATE').pick(:target)
  145. return date.to_s if !record
  146. record.sub!('dd', format('%<day>02d', day: date.day))
  147. record.sub!('d', date.day.to_s)
  148. record.sub!('mm', format('%<month>02d', month: date.month))
  149. record.sub!('m', date.month.to_s)
  150. record.sub!('yyyy', date.year.to_s)
  151. record.sub!('yy', date.year.to_s.last(2))
  152. record
  153. end
  154. def self.remote_translation_need_update?(raw, translations)
  155. translations.each do |row|
  156. next if row[1] != raw['locale']
  157. next if row[2] != raw['source']
  158. return false if row[3] == raw['target'] # no update if target is still the same
  159. return false if row[3] != row[4] # no update if translation has already changed
  160. return [true, Translation.find(row[0])]
  161. end
  162. [true, nil]
  163. end
  164. def self.import(locale, translations)
  165. bulk_import translations
  166. lang_cache_clear(locale)
  167. end
  168. def self.lang_cache_clear(locale)
  169. Cache.delete lang_cache_key(locale)
  170. end
  171. def self.lang_cache_set(locale, data)
  172. Cache.write lang_cache_key(locale), data
  173. end
  174. def self.lang_cache_get(locale)
  175. Cache.read lang_cache_key(locale)
  176. end
  177. private
  178. def set_initial
  179. return true if target_initial.present?
  180. return true if target_initial == ''
  181. self.target_initial = target
  182. true
  183. end
  184. def cache_delete
  185. super
  186. self.class.lang_cache_clear(locale) # delete whole lang cache as well
  187. end
  188. def self.lang_cache_key(locale)
  189. "TranslationMapOnlyContent::#{locale.downcase}"
  190. end
  191. private_class_method :lang_cache_key
  192. end