2022-01-01 13:38:12 +00:00
# Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
2021-11-15 15:58:19 +00:00
module Translation::SynchronizesFromPo
extend ActiveSupport :: Concern
2021-11-18 07:50:18 +00:00
TRANSLATION_FILE_STRUCT = Struct . new ( :translation , :translation_file , keyword_init : true ) . freeze
2021-11-15 15:58:19 +00:00
class_methods do # rubocop:disable Metrics/BlockLength
def sync
Locale . to_sync . each do | locale |
2021-11-19 12:11:20 +00:00
ActiveRecord :: Base . transaction do
sync_locale_from_po locale . locale
end
2021-11-15 15:58:19 +00:00
end
end
2021-11-19 12:11:20 +00:00
def sync_locale_from_po ( locale ) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
2021-11-15 15:58:19 +00:00
previous_unmodified_translations = Translation . where ( locale : locale , is_synchronized_from_codebase : true ) . select { | t | t . target . eql? ( t . target_initial ) }
updated_translation_ids = Set [ ]
2021-11-19 12:11:20 +00:00
importable_translations = [ ]
strings_for_locale ( locale ) . each_pair do | source , entry | # rubocop:disable Metrics/BlockLength
2021-11-26 07:08:02 +00:00
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 } ' "
2021-11-19 12:11:20 +00:00
next
end
2021-11-15 15:58:19 +00:00
t = Translation . find_source ( locale , source )
# New string
if ! t
2021-11-19 12:11:20 +00:00
importable_translations << Translation . new (
2021-11-15 15:58:19 +00:00
locale : locale ,
source : source ,
target : entry . translation ,
2021-11-19 12:11:20 +00:00
target_initial : entry . translation ,
2021-11-15 15:58:19 +00:00
is_synchronized_from_codebase : true ,
synchronized_from_translation_file : entry . translation_file ,
created_by_id : 1 ,
updated_by_id : 1
2021-11-19 12:11:20 +00:00
)
2021-11-15 15:58:19 +00:00
next
end
# Existing string
# Only change the target if it was not modified by the user
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
2021-11-19 12:11:20 +00:00
2022-03-16 16:35:50 +00:00
Translation . import locale , importable_translations
2021-11-15 15:58:19 +00:00
# Remove any unmodified & synchronized strings that are not present in the data any more.
previous_unmodified_translations . reject { | t | updated_translation_ids . member? t . id } . each ( & :destroy! )
true
end
def strings_for_locale ( locale ) # rubocop:disable Metrics/AbcSize
result = { }
po_files_for_locale ( locale ) . each do | file |
require 'poparser' # Only load when it is actually used
PoParser . parse_file ( Rails . root . join ( file ) ) . entries . each do | entry |
source = unescape_po ( entry . msgid . to_s )
# Make sure to ignore fuzzy entries.
translation = entry . translated? ? unescape_po ( entry . msgstr . to_s ) : ''
# For 'en-*' locales, treat source as translation as well, to indicate that nothing is missing.
translation = source if translation . empty? && locale . start_with? ( 'en' )
2021-11-18 07:50:18 +00:00
result [ source ] = TRANSLATION_FILE_STRUCT . new ( translation : translation , translation_file : file )
2021-11-15 15:58:19 +00:00
end
end
result
end
def unescape_po ( string )
string . gsub ( %r{ \\ n } , " \n " ) . gsub ( %r{ \\ " } , '"' ) . gsub ( %r{ \\ \\ } , '\\' )
end
# Returns all po files for a locale with zammad.*.po as first entry,
# followed by all other files in alphabetical order
# For en-us, i18n/zammad.pot will be returned instead.
def po_files_for_locale ( locale )
return [ 'i18n/zammad.pot' ] if locale . eql? 'en-us'
files = Dir . glob " i18n/*. #{ locale } .po " , base : Rails . root
2022-03-08 09:34:49 +00:00
if files . exclude? ( " i18n/zammad. #{ locale } .po " )
Rails . logger . error " No translation found for locale ' #{ locale } '. "
return [ ]
end
2021-11-15 15:58:19 +00:00
[
files . delete ( " i18n/zammad. #{ locale } .po " ) ,
* files . sort
]
end
end
end