diff --git a/service/lib/agama/dbus/software/manager.rb b/service/lib/agama/dbus/software/manager.rb index 7217d90811..aad196e3cb 100644 --- a/service/lib/agama/dbus/software/manager.rb +++ b/service/lib/agama/dbus/software/manager.rb @@ -149,31 +149,6 @@ def register_callbacks backend.on_issues_change { issues_properties_changed } end - # find translated product description if available - # @param data [Hash] product configuration from the YAML file - # @return [String,nil] Translated product description (if available) - # or the untranslated description, nil if not found - def localized_description(data) - translations = data["translations"]&.[]("description") - lang = ENV["LANG"] || "" - - # no translations or language not set, return untranslated value - return data["description"] if !translations.is_a?(Hash) || lang.empty? - - # remove the character encoding if present - lang = lang.split(".").first - # full matching (language + country) - return translations[lang] if translations[lang] - - # remove the country part - lang = lang.split("_").first - # partial match (just the language) - return translations[lang] if translations[lang] - - # fallback to original untranslated description - data["description"] - end - USER_SELECTED_PATTERN = 0 AUTO_SELECTED_PATTERN = 1 def compute_patterns diff --git a/service/lib/agama/dbus/software/product.rb b/service/lib/agama/dbus/software/product.rb index 3abfd98825..5085e42444 100644 --- a/service/lib/agama/dbus/software/product.rb +++ b/service/lib/agama/dbus/software/product.rb @@ -53,7 +53,7 @@ def issues def available_products backend.products.map do |product| - [product.id, product.display_name, { "description" => product.description }] + [product.id, product.display_name, { "description" => product.localized_description }] end end diff --git a/service/lib/agama/software/product.rb b/service/lib/agama/software/product.rb index 71f1ade8ac..24bb8c0b2b 100644 --- a/service/lib/agama/software/product.rb +++ b/service/lib/agama/software/product.rb @@ -30,22 +30,22 @@ class Product # Name of the product to be display. # - # @return [String] + # @return [String, nil] attr_accessor :display_name # Description of the product. # - # @return [String] + # @return [String, nil] attr_accessor :description # Internal name of the product. This is relevant for registering the product. # - # @return [String] + # @return [String, nil] attr_accessor :name # Version of the product. This is relevant for registering the product. # - # @return [String] E.g., "1.0". + # @return [String, nil] E.g., "1.0". attr_accessor :version # List of repositories. @@ -73,6 +73,19 @@ class Product # @return [Array] attr_accessor :optional_patterns + # Product translations. + # + # @example + # product.translations #=> + # { + # "description" => { + # "cs" => "Czech translation", + # "es" => "Spanish translation" + # } + # + # @return [Hash>] + attr_accessor :translations + # @param id [string] Product id. def initialize(id) @id = id @@ -81,6 +94,34 @@ def initialize(id) @optional_packages = [] @mandatory_patterns = [] @optional_patterns = [] + @translations = {} + end + + # Localized product description. + # + # If there is no translation for the current language, then the untranslated description is + # used. + # + # @return [String, nil] + def localized_description + translations = self.translations["description"] + lang = ENV["LANG"] + + # No translations or language not set, return untranslated value. + return description unless translations && lang + + # Remove the character encoding if present. + lang = lang.split(".").first + # Full matching (language + country) + return translations[lang] if translations[lang] + + # Remove the country part. + lang = lang.split("_").first + # Partial match (just the language). + return translations[lang] if translations[lang] + + # Fallback to original untranslated description. + description end end end diff --git a/service/lib/agama/software/product_builder.rb b/service/lib/agama/software/product_builder.rb index 0cd70a7473..b4ecfeddf8 100644 --- a/service/lib/agama/software/product_builder.rb +++ b/service/lib/agama/software/product_builder.rb @@ -47,6 +47,7 @@ def build product.optional_packages = data[:optional_packages] product.mandatory_patterns = data[:mandatory_patterns] product.optional_patterns = data[:optional_patterns] + product.translations = attrs["translations"] || {} end end end diff --git a/service/test/agama/dbus/software/manager_test.rb b/service/test/agama/dbus/software/manager_test.rb index ea94877819..925218b9d5 100644 --- a/service/test/agama/dbus/software/manager_test.rb +++ b/service/test/agama/dbus/software/manager_test.rb @@ -135,68 +135,4 @@ expect(installed).to eq(true) end end - - describe "#available_base_products" do - # testing product with translations - products = { - "Tumbleweed" => { - "name" => "openSUSE Tumbleweed", - "description" => "Original description", - "translations" => { - "description" => { - "cs" => "Czech translation", - "es" => "Spanish translation" - } - } - } - } - - it "returns product ID and name" do - expect(backend).to receive(:products).and_return(products) - - product = subject.available_base_products.first - expect(product[0]).to eq("Tumbleweed") - expect(product[1]).to eq("openSUSE Tumbleweed") - end - - it "returns untranslated description when the language is not set" do - allow(ENV).to receive(:[]).with("LANG").and_return(nil) - expect(backend).to receive(:products).and_return(products) - - product = subject.available_base_products.first - expect(product[2]["description"]).to eq("Original description") - end - - it "returns Czech translation if locale is \"cs_CZ.UTF-8\"" do - allow(ENV).to receive(:[]).with("LANG").and_return("cs_CZ.UTF-8") - expect(backend).to receive(:products).and_return(products) - - product = subject.available_base_products.first - expect(product[2]["description"]).to eq("Czech translation") - end - - it "returns Czech translation if locale is \"cs\"" do - allow(ENV).to receive(:[]).with("LANG").and_return("cs") - expect(backend).to receive(:products).and_return(products) - - product = subject.available_base_products.first - expect(product[2]["description"]).to eq("Czech translation") - end - - it "return untranslated description when translation is not available" do - allow(ENV).to receive(:[]).with("LANG").and_return("cs_CZ.UTF-8") - - # testing product without translations - untranslated = { - "Tumbleweed" => { - "name" => "openSUSE Tumbleweed", - "description" => "Original description" - } - } - expect(backend).to receive(:products).and_return(untranslated) - - product = subject.available_base_products.first - expect(product[2]["description"]).to eq("Original description") - end - end end diff --git a/service/test/agama/software/product_builder_test.rb b/service/test/agama/software/product_builder_test.rb index f7bf6b48c2..a48bc87ceb 100644 --- a/service/test/agama/software/product_builder_test.rb +++ b/service/test/agama/software/product_builder_test.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Copyright (c) [2022-2023] SUSE LLC +# Copyright (c) [2023] SUSE LLC # # All Rights Reserved. # @@ -38,10 +38,16 @@ let(:products) do [ { - "id" => "Test1", - "name" => "Product Test 1", - "description" => "This is a test product named Test 1", - "software" => { + "id" => "Test1", + "name" => "Product Test 1", + "description" => "This is a test product named Test 1", + "translations" => { + "description" => { + "cs" => "Czech", + "es" => "Spanish" + } + }, + "software" => { "installation_repositories" => [ { "url" => "https://repos/test1/x86_64/product/", @@ -141,7 +147,8 @@ mandatory_patterns: ["pattern1-1", "pattern1-2"], optional_patterns: ["pattern1-3"], mandatory_packages: ["package1-1", "package1-2", "package1-3"], - optional_packages: ["package1-5"] + optional_packages: ["package1-5"], + translations: { "description" => { "cs" => "Czech", "es" => "Spanish" } } ), an_object_having_attributes( id: "Test2", @@ -153,7 +160,8 @@ mandatory_patterns: ["pattern2-1"], optional_patterns: [], mandatory_packages: [], - optional_packages: [] + optional_packages: [], + translations: {} ) ) end @@ -183,7 +191,8 @@ mandatory_patterns: ["pattern1-1", "pattern1-2"], optional_patterns: ["pattern1-4"], mandatory_packages: ["package1-1", "package1-2", "package1-3"], - optional_packages: ["package1-5"] + optional_packages: ["package1-5"], + translations: { "description" => { "cs" => "Czech", "es" => "Spanish" } } ), an_object_having_attributes( id: "Test2", @@ -195,7 +204,8 @@ mandatory_patterns: ["pattern2-1"], optional_patterns: [], mandatory_packages: [], - optional_packages: [] + optional_packages: [], + translations: {} ), an_object_having_attributes( id: "Test3", @@ -207,7 +217,8 @@ mandatory_patterns: [], optional_patterns: ["pattern3-1"], mandatory_packages: [], - optional_packages: [] + optional_packages: [], + translations: {} ) ) end @@ -237,7 +248,8 @@ mandatory_patterns: ["pattern1-1", "pattern1-2"], optional_patterns: [], mandatory_packages: ["package1-1", "package1-2", "package1-4"], - optional_packages: ["package1-5"] + optional_packages: ["package1-5"], + translations: { "description" => { "cs" => "Czech", "es" => "Spanish" } } ), an_object_having_attributes( id: "Test3", @@ -249,7 +261,8 @@ mandatory_patterns: [], optional_patterns: [], mandatory_packages: [], - optional_packages: [] + optional_packages: [], + translations: {} ) ) end @@ -279,7 +292,8 @@ mandatory_patterns: ["pattern1-1", "pattern1-2"], optional_patterns: [], mandatory_packages: ["package1-1", "package1-2"], - optional_packages: ["package1-5"] + optional_packages: ["package1-5"], + translations: { "description" => { "cs" => "Czech", "es" => "Spanish" } } ) ) end diff --git a/service/test/agama/software/product_test.rb b/service/test/agama/software/product_test.rb new file mode 100644 index 0000000000..aab6a8397e --- /dev/null +++ b/service/test/agama/software/product_test.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +# Copyright (c) [2023] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" +require "agama/software/product" + +describe Agama::Software::Product do + subject { described_class.new("Test") } + + describe "#localized_description" do + before do + subject.description = "Original description" + subject.translations = { + "description" => { + "cs" => "Czech translation", + "es" => "Spanish translation" + } + } + end + + it "returns untranslated description when the language is not set" do + allow(ENV).to receive(:[]).with("LANG").and_return(nil) + + expect(subject.localized_description).to eq("Original description") + end + + it "returns Czech translation if locale is \"cs_CZ.UTF-8\"" do + allow(ENV).to receive(:[]).with("LANG").and_return("cs_CZ.UTF-8") + + expect(subject.localized_description).to eq("Czech translation") + end + + it "returns Czech translation if locale is \"cs\"" do + allow(ENV).to receive(:[]).with("LANG").and_return("cs") + + expect(subject.localized_description).to eq("Czech translation") + end + + it "return untranslated description when translation is not available" do + allow(ENV).to receive(:[]).with("LANG").and_return("cs_CZ.UTF-8") + subject.translations = {} + + expect(subject.localized_description).to eq("Original description") + end + end +end