123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424 |
- # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
- require 'csv'
- class Translation < ApplicationModel
- before_create :set_initial
- after_create :cache_clear
- after_update :cache_clear
- after_destroy :cache_clear
- =begin
- sync translations from local if exists, otherwise from online
- all:
- Translation.sync
- Translation.sync(locale) # e. g. 'en-us' or 'de-de'
- =end
- def self.sync(dedicated_locale = nil)
- return true if load_from_file(dedicated_locale)
- load
- end
- =begin
- load translations from online
- all:
- Translation.load
- dedicated:
- Translation.load(locale) # e. g. 'en-us' or 'de-de'
- =end
- def self.load(dedicated_locale = nil)
- locals_to_sync(dedicated_locale).each do |locale|
- fetch(locale)
- load_from_file(locale)
- end
- true
- end
- =begin
- push translations to online
- Translation.push(locale)
- =end
- def self.push(locale)
- # only push changed translations
- translations = Translation.where(locale: locale)
- translations_to_push = []
- translations.each do |translation|
- if translation.target != translation.target_initial
- translations_to_push.push translation
- end
- end
- return true if translations_to_push.empty?
- url = 'https://i18n.zammad.com/api/v1/translations/thanks_for_your_support'
- translator_key = Setting.get('translator_key')
- result = UserAgent.post(
- url,
- {
- locale: locale,
- translations: translations_to_push,
- fqdn: Setting.get('fqdn'),
- translator_key: translator_key,
- },
- {
- json: true,
- open_timeout: 8,
- read_timeout: 24,
- }
- )
- raise "Can't push translations to #{url}: #{result.error}" if !result.success?
- # set new translator_key if given
- if result.data['translator_key']
- translator_key = Setting.set('translator_key', result.data['translator_key'])
- end
- true
- end
- =begin
- reset translations to origin
- Translation.reset(locale)
- =end
- def self.reset(locale)
- # only push changed translations
- translations = Translation.where(locale: locale)
- translations.each do |translation|
- if !translation.target_initial || translation.target_initial.empty?
- translation.destroy
- elsif translation.target != translation.target_initial
- translation.target = translation.target_initial
- translation.save
- end
- end
- true
- end
- =begin
- get list of translations
- list = Translation.lang('de-de')
- =end
- def self.lang(locale, admin = false)
- # use cache if not admin page is requested
- if !admin
- data = cache_get(locale)
- return data if data
- end
- # show total translations as reference count
- data = {
- 'total' => Translation.where(locale: 'de-de').count,
- }
- list = []
- translations = if admin
- Translation.where(locale: locale.downcase).order(:source)
- else
- Translation.where(locale: locale.downcase).where.not(target: '').order(:source)
- end
- translations.each do |item|
- translation_item = []
- translation_item = if admin
- [
- item.id,
- item.source,
- item.target,
- item.target_initial,
- item.format,
- ]
- else
- [
- item.id,
- item.source,
- item.target,
- item.format,
- ]
- end
- list.push translation_item
- end
- # add presorted on top
- presorted_list = []
- %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|
- list.each do |item|
- next if item[1] != presort
- presorted_list.push item
- list.delete item
- #list.unshift presort
- end
- end
- data['list'] = presorted_list.concat list
- # set cache
- if !admin
- cache_set(locale, data)
- end
- data
- end
- =begin
- translate strings in ruby context, e. g. for notifications
- translated = Translation.translate('de-de', 'New')
- =end
- def self.translate(locale, string)
- # translate string
- records = Translation.where(locale: locale, source: string)
- records.each do |record|
- return record.target if record.source == string
- end
- # fallback lookup in en
- records = Translation.where(locale: 'en', source: string)
- records.each do |record|
- return record.target if record.source == string
- end
- string
- end
- =begin
- load translations from local
- all:
- Translation.load_from_file
- or
- Translation.load_from_file(locale) # e. g. 'en-us' or 'de-de'
- =end
- def self.load_from_file(dedicated_locale = nil)
- version = Version.get
- directory = Rails.root.join('config/translations')
- locals_to_sync(dedicated_locale).each do |locale|
- file = Rails.root.join("#{directory}/#{locale}-#{version}.yml")
- return false if !File.exist?(file)
- data = YAML.load_file(file)
- to_database(locale, data)
- end
- true
- end
- =begin
- fetch translation from remote and store them in local file system
- all:
- Translation.fetch
- or
- Translation.fetch(locale) # e. g. 'en-us' or 'de-de'
- =end
- def self.fetch(dedicated_locale = nil)
- version = Version.get
- locals_to_sync(dedicated_locale).each do |locale|
- url = "https://i18n.zammad.com/api/v1/translations/#{locale}"
- if !UserInfo.current_user_id
- UserInfo.current_user_id = 1
- end
- result = UserAgent.get(
- url,
- {
- version: version,
- },
- {
- json: true,
- open_timeout: 8,
- read_timeout: 24,
- }
- )
- raise "Can't load translations from #{url}: #{result.error}" if !result.success?
- directory = Rails.root.join('config/translations')
- if !File.directory?(directory)
- Dir.mkdir(directory, 0o755)
- end
- file = Rails.root.join("#{directory}/#{locale}-#{version}.yml")
- File.open(file, 'w') do |out|
- YAML.dump(result.data, out)
- end
- end
- true
- end
- =begin
- load translations from csv file
- all:
- Translation.load_from_csv
- or
- Translation.load_from_csv(locale, file_location, file_charset) # e. g. 'en-us' or 'de-de' and /path/to/translation_list.csv
- e. g.
- Translation.load_from_csv('he-il', '/Users/me/Downloads/Hebrew_translation_list-1.csv', 'Windows-1255')
- Get source file at https://i18n.zammad.com/api/v1/translations_empty_translation_list
- =end
- def self.load_from_csv(locale_name, location, charset = 'UTF8')
- locale = Locale.find_by(locale: locale_name)
- if !locale
- raise "No such locale: #{locale_name}"
- end
- if !::File.exist?(location)
- raise "No such file: #{location}"
- end
- content = ::File.open(location, "r:#{charset}").read
- params = {
- col_sep: ',',
- }
- rows = ::CSV.parse(content, params)
- header = rows.shift
- translation_raw = []
- rows.each do |row|
- raise "Can't import translation, source is missing" if row[0].blank?
- if row[1].blank?
- warn "Skipped #{row[0]}, because translation is blank"
- next
- end
- raise "Can't import translation, format is missing" if row[2].blank?
- raise "Can't import translation, format is invalid (#{row[2]})" if row[2] !~ /^(time|string)$/
- item = {
- 'locale' => locale.locale,
- 'source' => row[0],
- 'target' => row[1],
- 'target_initial' => '',
- 'format' => row[2],
- }
- translation_raw.push item
- end
- to_database(locale.name, translation_raw)
- true
- end
- private_class_method def self.to_database(locale, data)
- translations = Translation.where(locale: locale).all
- ActiveRecord::Base.transaction do
- data.each do |translation_raw|
- # handle case insensitive sql
- translation = nil
- translations.each do |item|
- next if item.format != translation_raw['format']
- next if item.source != translation_raw['source']
- translation = item
- break
- end
- if translation
- # verify if update is needed
- update_needed = false
- translation_raw.each do |key, _value|
- # if translation target has changes
- next unless translation_raw[key] != translation.target
- # do not update translations which are already changed by user
- if translation.target == translation.target_initial
- update_needed = true
- break
- end
- end
- if update_needed
- translation.update!(translation_raw.symbolize_keys!)
- translation.save
- end
- else
- if !UserInfo.current_user_id
- translation_raw['updated_by_id'] = 1
- translation_raw['created_by_id'] = 1
- end
- Translation.create(translation_raw.symbolize_keys!)
- end
- end
- end
- end
- private_class_method def self.locals_to_sync(dedicated_locale = nil)
- locales_list = []
- if !dedicated_locale
- locales = Locale.to_sync
- locales.each do |locale|
- locales_list.push locale.locale
- end
- else
- locales_list = [dedicated_locale]
- end
- locales_list
- end
- private
- def set_initial
- return true if target_initial
- return true if target_initial == ''
- self.target_initial = target
- true
- end
- def cache_clear
- Cache.delete('TranslationMapOnlyContent::' + locale.downcase)
- true
- end
- def self.cache_set(locale, data)
- Cache.write('TranslationMapOnlyContent::' + locale.downcase, data)
- end
- private_class_method :cache_set
- def self.cache_get(locale)
- Cache.get('TranslationMapOnlyContent::' + locale.downcase)
- end
- private_class_method :cache_get
- end
|