Browse Source

Maintenance: Improve translation toolchain.

Martin Gruner 2 years ago
parent
commit
2081062b6d

+ 4 - 0
doc/developer_manual/standards/how-to-handle-localization.md

@@ -55,6 +55,10 @@ This will update the `i18n/zammad.pot` file in Zammad. To do this for an existin
 
 `rails generate zammad:translation_catalog --addon-path /path/to/addon`
 
+To perform additional tasks such as updating template files from translations, use:
+
+`rails generate zammad:translation_catalog --full`
+
 ### Testing Translation Changes from Weblate
 
 To test any changes made to translations in Weblate that are not yet published to Zammad,

+ 22 - 0
lib/generators/zammad/translation_catalog/writer/base.rb

@@ -3,10 +3,32 @@
 class Zammad::TranslationCatalog::Writer::Base
   attr_reader :options
 
+  class << self
+    def optional(value)
+      @optional = value
+    end
+
+    def optional?
+      @optional
+    end
+  end
+
   def initialize(options:)
     @options = options
   end
 
+  def skip?
+    return false if !self.class.optional?
+
+    # Only execute for Zammad, not for addons.
+    return true if options['addon_path']
+
+    # Do not run in CI.
+    return true if options['check']
+
+    !@options['full']
+  end
+
   protected
 
   def create_or_update_file(file, content)

+ 2 - 7
lib/generators/zammad/translation_catalog/writer/chat.rb

@@ -2,14 +2,9 @@
 
 class Zammad::TranslationCatalog::Writer::Chat < Zammad::TranslationCatalog::Writer::Base
 
-  def write(extracted_strings)
-
-    # Only execute for Zammad, not for addons.
-    return if options['addon_path']
-
-    # Do not run in CI.
-    return if options['check']
+  optional true
 
+  def write(extracted_strings)
     content = serialized(translation_map(extracted_strings))
     ['public/assets/chat/chat.coffee', 'public/assets/chat/chat-no-jquery.coffee'].each do |f|
       write_file(f, content)

+ 2 - 7
lib/generators/zammad/translation_catalog/writer/form_js.rb

@@ -2,14 +2,9 @@
 
 class Zammad::TranslationCatalog::Writer::FormJs < Zammad::TranslationCatalog::Writer::Base
 
-  def write(extracted_strings)
-
-    # Only execute for Zammad, not for addons.
-    return if options['addon_path']
-
-    # Do not run in CI.
-    return if options['check']
+  optional true
 
+  def write(extracted_strings)
     content = serialized(translation_map(extracted_strings))
     write_file('public/assets/form/form.js', content)
   end

+ 2 - 0
lib/generators/zammad/translation_catalog/writer/pot.rb

@@ -2,6 +2,8 @@
 
 class Zammad::TranslationCatalog::Writer::Pot < Zammad::TranslationCatalog::Writer::Base
 
+  optional false
+
   def write(extracted_strings)
 
     pot = build_pot_content(extracted_strings)

+ 45 - 0
lib/generators/zammad/translation_catalog/writer/verify_locale_entries.rb

@@ -0,0 +1,45 @@
+# Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
+
+class Zammad::TranslationCatalog::Writer::VerifyLocaleEntries < Zammad::TranslationCatalog::Writer::Base
+
+  optional true
+
+  # Languages with a completeness over this threshold must have an entry in locales.yml.
+  COMPLETENESS_THRESHOLD_PERCENT = 20
+
+  def write(_extracted_strings)
+    missing_locale_entries?
+    missing_language_files?
+  end
+
+  private
+
+  def languages
+    @languages ||= Rails.root.join('i18n').glob('zammad.*.po').map { |file| file.basename.to_s.split('.')[-2] }
+  end
+
+  def missing_locale_entries?
+    languages.each do |l|
+      next if Locale.exists?(locale: l)
+
+      strings = Translation.cached_strings_for_locale(l).values
+      completeness = ((strings.count { |s| s.translation.present? } / strings.count) * 100).to_i
+
+      next if completeness < COMPLETENESS_THRESHOLD_PERCENT
+
+      warn "Warning: language '#{l}' is #{completeness} % translated, but has no locale yet."
+    end
+  end
+
+  def missing_language_files?
+
+    skippable_locales = %w[en-us sr-latn-rs]
+
+    Locale.all.each do |l|
+      next if languages.include? l.locale
+      next if skippable_locales.include? l.locale
+
+      warn "Warning: locale '#{l.locale}' has no corresponding translation file."
+    end
+  end
+end

+ 7 - 11
lib/generators/zammad/translation_catalog/writer/view_templates.rb

@@ -2,18 +2,10 @@
 
 class Zammad::TranslationCatalog::Writer::ViewTemplates < Zammad::TranslationCatalog::Writer::Base
 
-  def write(_strings)
-
-    # Only execute for Zammad, not for addons.
-    return if options['addon_path']
-
-    # Do not run in CI.
-    return if options['check']
-
-    extractor = Zammad::TranslationCatalog::Extractor::ViewTemplates.new(options: options)
-    extractor.extract_translatable_strings
+  optional true
 
-    extractor.extracted_strings.sorted_values.each do |extracted_string|
+  def write(_strings)
+    extracted_strings.sorted_values.each do |extracted_string|
       Locale.all.each do |locale|
         next if locale.locale.start_with?('en')
 
@@ -24,6 +16,10 @@ class Zammad::TranslationCatalog::Writer::ViewTemplates < Zammad::TranslationCat
 
   private
 
+  def extracted_strings
+    Zammad::TranslationCatalog::Extractor::ViewTemplates.new(options: options).tap(&:extract_translatable_strings).extracted_strings
+  end
+
   def handle_template(extracted_string, locale)
     target_filename = extracted_string.references.first.sub(%r{/en.}, "/#{locale.alias.presence || locale.locale}.")
     translation = Translation.cached_strings_for_locale(locale.locale)[extracted_string.string]&.translation

+ 6 - 1
lib/generators/zammad/translation_catalog_generator.rb

@@ -14,10 +14,14 @@ class Zammad::TranslationCatalogGenerator < Rails::Generators::Base
 
         # Regenerate for an addon
         rails generate zammad:translation_catalog --addon-path /path/to/addon
+
+        # Perform additional tasks such as updating template files from translations
+        rails generate zammad:translation_catalog --full
   DESCRIPTION
 
   class_option :check, type: :boolean, required: false, desc: 'Only check if the catalog file is up-to-date.'
   class_option :addon_path, type: :string, required: false, banner: '/path/to/addon', desc: 'Generate catalog for the specified addon module only.'
+  class_option :full, type: :boolean, required: false, desc: 'Perform additional tasks such as updating template files from translations.'
 
   # Make sure .descendants always has the full list.
   Mixin::RequiredSubPaths.eager_load_recursive Zammad::TranslationCatalog, "#{__dir__}/translation_catalog"
@@ -56,7 +60,8 @@ class Zammad::TranslationCatalogGenerator < Rails::Generators::Base
 
   def write_strings(extracted_strings)
     Zammad::TranslationCatalog::Writer::Base.descendants.each do |klass|
-      klass.new(options: options).write(extracted_strings)
+      writer = klass.new(options: options)
+      writer.write(extracted_strings) if !writer.skip?
     end
   end