translation.rb 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. # Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
  2. class Translation < ApplicationModel
  3. include Translation::SynchronizesFromPo
  4. before_create :set_initial
  5. validates :locale, presence: true
  6. =begin
  7. reset translations to origin
  8. Translation.reset(locale)
  9. =end
  10. def self.reset(locale)
  11. # only push changed translations
  12. translations = Translation.where(locale: locale)
  13. translations.each do |translation|
  14. if translation.target_initial.blank?
  15. translation.destroy
  16. elsif translation.target != translation.target_initial
  17. translation.target = translation.target_initial
  18. translation.save
  19. end
  20. end
  21. true
  22. end
  23. =begin
  24. get list of translations
  25. list = Translation.lang('de-de')
  26. =end
  27. def self.lang(locale, admin = false)
  28. Rails.cache.fetch("#{self}/#{latest_change}/lang/#{locale}/#{admin}") do
  29. # show total translations as reference count
  30. data = {
  31. 'total' => Translation.where(locale: 'de-de').count,
  32. }
  33. list = []
  34. translations = if admin
  35. Translation.where(locale: locale.downcase).order(:source)
  36. else
  37. Translation.where(locale: locale.downcase).where.not(target: '').order(:source)
  38. end
  39. translations.each do |item|
  40. translation_item = if admin
  41. [
  42. item.id,
  43. item.source,
  44. item.target,
  45. item.target_initial,
  46. ]
  47. else
  48. [
  49. item.id,
  50. item.source,
  51. item.target,
  52. ]
  53. end
  54. list.push translation_item
  55. end
  56. # add presorted on top
  57. presorted_list = []
  58. %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|
  59. list.each do |item|
  60. next if item[1] != presort
  61. presorted_list.push item
  62. list.delete item
  63. # list.unshift presort
  64. end
  65. end
  66. data['list'] = presorted_list.concat list
  67. data
  68. end
  69. end
  70. =begin
  71. translate strings in Ruby context, e. g. for notifications
  72. translated = Translation.translate('de-de', 'New')
  73. =end
  74. def self.translate(locale, string, *args)
  75. translated = find_source(locale, string)&.target || string
  76. translated = translated % args if args.any?
  77. translated
  78. end
  79. =begin
  80. find a translation record for a given locale and source string. 'find_by' might not be enough,
  81. because it could produce wrong matches on case insensitive MySQL databases.
  82. =end
  83. def self.find_source(locale, string)
  84. if ActiveRecord::Base.connection_db_config.configuration_hash[:adapter] == 'mysql2'
  85. # MySQL might find the wrong record with find_by with case insensitive locales, so use a direct comparison.
  86. where(locale: locale, source: string).find { |t| t.source.eql? string }
  87. else
  88. find_by(locale: locale, source: string)
  89. end
  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, append_timezone: true)
  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})" if append_timezone
  126. record
  127. end
  128. =begin
  129. translate date in ruby context, e. g. for notifications
  130. translated = Translation.date('de-de', '2018-10-10')
  131. or
  132. translated = Translation.date('de-de', Date.parse('2018-10-10'))
  133. =end
  134. def self.date(locale, date)
  135. if date.instance_of?(String)
  136. begin
  137. date_parsed = Date.parse(date)
  138. return date.to_s if !date_parsed
  139. date = date_parsed
  140. rescue
  141. return date.to_s
  142. end
  143. end
  144. return date.to_s if date.class != Date
  145. record = Translation.where(locale: locale, source: 'FORMAT_DATE').pick(:target)
  146. return date.to_s if !record
  147. record.sub!('dd', format('%<day>02d', day: date.day))
  148. record.sub!('d', date.day.to_s)
  149. record.sub!('mm', format('%<month>02d', month: date.month))
  150. record.sub!('m', date.month.to_s)
  151. record.sub!('yyyy', date.year.to_s)
  152. record.sub!('yy', date.year.to_s.last(2))
  153. record
  154. end
  155. def self.remote_translation_need_update?(raw, translations)
  156. translations.each do |row|
  157. next if row[1] != raw['locale']
  158. next if row[2] != raw['source']
  159. return false if row[3] == raw['target'] # no update if target is still the same
  160. return false if row[3] != row[4] # no update if translation has already changed
  161. return [true, Translation.find(row[0])]
  162. end
  163. [true, nil]
  164. end
  165. private
  166. def set_initial
  167. return true if target_initial.present?
  168. return true if target_initial == ''
  169. self.target_initial = target
  170. true
  171. end
  172. end