123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- module Translation::SynchronizesFromPo
- extend ActiveSupport::Concern
-
- class TranslationEntry
- attr_reader :source, :translation_file, :skip_sync
- attr_accessor :translation
- def self.create(locale, file, entry)
- source = unescape_po(entry.msgid.to_s)
-
- translation = entry.translated? ? unescape_po(entry.msgstr.to_s) : ''
-
- translation = source if translation.empty? && locale.start_with?('en')
-
-
- skip_sync = entry.reference.present? && [entry.reference].flatten.all? do |ref|
-
-
-
- ref.to_s.start_with?(%r{public/assets/(?:chat|form)/|app/views/(?:mailer|messaging)/[^/]+/})
- end
- new(source: source, translation: translation, translation_file: file, skip_sync: skip_sync)
- end
- def self.unescape_po(string)
- string.gsub(%r{\\n}, "\n").gsub(%r{\\"}, '"').gsub(%r{\\\\}, '\\')
- end
- def skip_sync?
- @skip_sync
- end
- private
- def initialize(source:, translation:, translation_file:, skip_sync:)
- @source = source
- @translation = translation
- @translation_file = translation_file
- @skip_sync = skip_sync
- end
- end
- class_methods do
- def sync
- Locale.to_sync.each do |locale|
- ActiveRecord::Base.transaction do
- sync_locale_from_po locale.locale
- end
- end
- end
- def sync_locale_from_po(locale)
- previous_translation_map = Translation.where(locale: locale).index_by(&:source)
- previous_unmodified_translations = Translation.where(locale: locale, is_synchronized_from_codebase: true).select { |t| t.target.eql?(t.target_initial) }
- updated_translation_ids = Set[]
- importable_translations = []
- strings_for_locale(locale).each_pair do |source, entry|
- next if entry.skip_sync?
- if source.length > 3000 || entry.translation.length > 3000
- Rails.logger.error "Cannot import translation for locale #{locale} because it exceeds maximum string length of 3000: source: '#{source}', translation: '#{entry.translation}'"
- next
- end
- t = previous_translation_map[source]
-
- if !t
- importable_translations << Translation.new(
- locale: locale,
- source: source,
- target: entry.translation,
- target_initial: entry.translation,
- is_synchronized_from_codebase: true,
- synchronized_from_translation_file: entry.translation_file,
- created_by_id: 1,
- updated_by_id: 1
- )
- next
- end
-
-
- t.target = entry.translation if t.target.eql? t.target_initial
- t.is_synchronized_from_codebase = true
- t.synchronized_from_translation_file = entry.translation_file
- t.target_initial = entry.translation
- if t.changed.present?
- t.updated_by_id = 1
- t.save!
- end
- updated_translation_ids.add t.id
- end
- Translation.bulk_import importable_translations
-
- previous_unmodified_translations.reject { |t| updated_translation_ids.member? t.id }.each(&:destroy!)
- true
- end
- def cached_strings_for_locale(locale)
- @cached_strings_for_locale ||= {}
- @cached_strings_for_locale[locale] ||= strings_for_locale(locale).freeze
- end
- def strings_for_locale(locale)
- result = {}
- po_files_for_locale(locale).each do |file|
- require 'poparser'
- PoParser.parse_file(Rails.root.join(file)).entries.each do |entry|
- TranslationEntry.create(locale, file, entry).tap do |translation_entry|
-
- if locale == 'sr-latn-rs'
- require 'byk/safe'
- translation_entry.translation = Byk.to_latin(translation_entry.translation)
- end
- result[translation_entry.source] = translation_entry
- end
- end
- end
- result
- end
-
-
-
- def po_files_for_locale(locale)
- return ['i18n/zammad.pot'] if locale.eql? 'en-us'
- locale_name = locale
-
- locale_name = 'sr-cyrl-rs' if locale_name == 'sr-latn-rs'
- files = Dir.glob "i18n/*.#{locale_name}.po", base: Rails.root
- if files.exclude?("i18n/zammad.#{locale_name}.po")
- Rails.logger.error "No translation found for locale '#{locale_name}'."
- return []
- end
- [
- files.delete("i18n/zammad.#{locale_name}.po"),
- *files.sort
- ]
- end
- end
- end
|