| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/on_device_translation/language_pack_util.h" |
| |
| #include <string_view> |
| |
| #include "base/check_op.h" |
| #include "base/containers/fixed_flat_map.h" |
| #include "base/strings/strcat.h" |
| #include "chrome/browser/on_device_translation/component_manager.h" |
| |
| namespace on_device_translation { |
| |
| LanguagePackRequirements::LanguagePackRequirements() = default; |
| LanguagePackRequirements::~LanguagePackRequirements() = default; |
| LanguagePackRequirements::LanguagePackRequirements( |
| LanguagePackRequirements&&) noexcept = default; |
| LanguagePackRequirements& LanguagePackRequirements::operator=( |
| LanguagePackRequirements&&) noexcept = default; |
| |
| namespace { |
| |
| constexpr char kPrefNamePrefix[] = |
| "on_device_translation.translate_kit_packages."; |
| constexpr char kComponentPathPrefNameSuffix[] = "_path"; |
| constexpr char kRegisteredFlagPrefNameSuffix[] = "_registered"; |
| |
| // Currently we always translate via English, so the number of |
| // SupportedLanguages needs to include English in addition to all the |
| // LanguagePackKeys. |
| static_assert(static_cast<unsigned>(SupportedLanguage::kMaxValue) == |
| static_cast<unsigned>(LanguagePackKey::kMaxValue) + 1, |
| "Missmatching SupportedLanguage size and LanguagePackKey size"); |
| |
| // Map from `SupportedLanguage` to the language code. |
| inline constexpr auto kSupportedLanguageCodeMap = base::MakeFixedFlatMap< |
| SupportedLanguage, |
| std::string_view>( |
| {{SupportedLanguage::kEn, "en"}, {SupportedLanguage::kEs, "es"}, |
| {SupportedLanguage::kJa, "ja"}, {SupportedLanguage::kAr, "ar"}, |
| {SupportedLanguage::kBn, "bn"}, {SupportedLanguage::kDe, "de"}, |
| {SupportedLanguage::kFr, "fr"}, {SupportedLanguage::kHi, "hi"}, |
| {SupportedLanguage::kIt, "it"}, {SupportedLanguage::kKo, "ko"}, |
| {SupportedLanguage::kNl, "nl"}, {SupportedLanguage::kPl, "pl"}, |
| {SupportedLanguage::kPt, "pt"}, {SupportedLanguage::kRu, "ru"}, |
| {SupportedLanguage::kTh, "th"}, {SupportedLanguage::kTr, "tr"}, |
| {SupportedLanguage::kVi, "vi"}, {SupportedLanguage::kZh, "zh"}, |
| {SupportedLanguage::kZhHant, "zh-Hant"}, {SupportedLanguage::kBg, "bg"}, |
| {SupportedLanguage::kCs, "cs"}, {SupportedLanguage::kDa, "da"}, |
| {SupportedLanguage::kEl, "el"}, {SupportedLanguage::kFi, "fi"}, |
| {SupportedLanguage::kHr, "hr"}, {SupportedLanguage::kHu, "hu"}, |
| {SupportedLanguage::kId, "id"}, {SupportedLanguage::kIw, "iw"}, |
| {SupportedLanguage::kLt, "lt"}, {SupportedLanguage::kNo, "no"}, |
| {SupportedLanguage::kRo, "ro"}, {SupportedLanguage::kSk, "sk"}, |
| {SupportedLanguage::kSl, "sl"}, {SupportedLanguage::kSv, "sv"}, |
| {SupportedLanguage::kUk, "uk"}, {SupportedLanguage::kKn, "kn"}, |
| {SupportedLanguage::kTa, "ta"}, {SupportedLanguage::kTe, "te"}, |
| {SupportedLanguage::kMr, "mr"}}); |
| static_assert(std::size(kSupportedLanguageCodeMap) == |
| static_cast<unsigned>(SupportedLanguage::kMaxValue) + 1, |
| "All languages must be in kSupportedLanguageCodeMap."); |
| |
| // Returns the language pair for a non-English supported language. The order of |
| // the language pair is determined by the language code's alphabetical order. |
| inline std::pair<SupportedLanguage, SupportedLanguage> |
| SupportedLanguagePairFromNonEnglishSupportedLanguage( |
| SupportedLanguage supported_language) { |
| CHECK_NE(supported_language, SupportedLanguage::kEn); |
| if (kSupportedLanguageCodeMap.at(supported_language) < "en") { |
| return std::make_pair(supported_language, SupportedLanguage::kEn); |
| } else { |
| return std::make_pair(SupportedLanguage::kEn, supported_language); |
| } |
| } |
| |
| // The inverse of kSupportedLanguageCodeMap. |
| inline constexpr auto kSupportedLanguageCodeInverseMap = base::MakeFixedFlatMap< |
| std::string_view, |
| SupportedLanguage>( |
| {{"en", SupportedLanguage::kEn}, {"es", SupportedLanguage::kEs}, |
| {"ja", SupportedLanguage::kJa}, {"ar", SupportedLanguage::kAr}, |
| {"bn", SupportedLanguage::kBn}, {"de", SupportedLanguage::kDe}, |
| {"fr", SupportedLanguage::kFr}, {"hi", SupportedLanguage::kHi}, |
| {"it", SupportedLanguage::kIt}, {"ko", SupportedLanguage::kKo}, |
| {"nl", SupportedLanguage::kNl}, {"pl", SupportedLanguage::kPl}, |
| {"pt", SupportedLanguage::kPt}, {"ru", SupportedLanguage::kRu}, |
| {"th", SupportedLanguage::kTh}, {"tr", SupportedLanguage::kTr}, |
| {"vi", SupportedLanguage::kVi}, {"zh", SupportedLanguage::kZh}, |
| {"zh-Hant", SupportedLanguage::kZhHant}, {"bg", SupportedLanguage::kBg}, |
| {"cs", SupportedLanguage::kCs}, {"da", SupportedLanguage::kDa}, |
| {"el", SupportedLanguage::kEl}, {"fi", SupportedLanguage::kFi}, |
| {"hr", SupportedLanguage::kHr}, {"hu", SupportedLanguage::kHu}, |
| {"id", SupportedLanguage::kId}, {"iw", SupportedLanguage::kIw}, |
| {"lt", SupportedLanguage::kLt}, {"no", SupportedLanguage::kNo}, |
| {"ro", SupportedLanguage::kRo}, {"sk", SupportedLanguage::kSk}, |
| {"sl", SupportedLanguage::kSl}, {"sv", SupportedLanguage::kSv}, |
| {"uk", SupportedLanguage::kUk}, {"kn", SupportedLanguage::kKn}, |
| {"ta", SupportedLanguage::kTa}, {"te", SupportedLanguage::kTe}, |
| {"mr", SupportedLanguage::kMr}}); |
| static_assert(std::size(kSupportedLanguageCodeInverseMap) == |
| static_cast<unsigned>(SupportedLanguage::kMaxValue) + 1, |
| "All languages must be in kSupportedLanguageCodeInverseMap."); |
| |
| LanguagePackKey LanguagePackKeyFromNonEnglishSupportedLanguage( |
| SupportedLanguage supported_language) { |
| CHECK_NE(supported_language, SupportedLanguage::kEn); |
| return static_cast<LanguagePackKey>( |
| static_cast<unsigned>(supported_language) - 1); |
| } |
| |
| } // namespace |
| |
| // Converts a SupportedLanguage to a language code. |
| std::string_view ToLanguageCode(SupportedLanguage supported_language) { |
| return kSupportedLanguageCodeMap.at(supported_language); |
| } |
| |
| // Converts a language code to a SupportedLanguage. |
| std::optional<SupportedLanguage> ToSupportedLanguage( |
| std::string_view language_code) { |
| auto it = kSupportedLanguageCodeInverseMap.find(language_code); |
| if (it != kSupportedLanguageCodeInverseMap.end()) { |
| return it->second; |
| } |
| return std::nullopt; |
| } |
| |
| SupportedLanguage NonEnglishSupportedLanguageFromLanguagePackKey( |
| LanguagePackKey language_pack_key) { |
| return static_cast<SupportedLanguage>( |
| static_cast<unsigned>(language_pack_key) + 1); |
| } |
| |
| std::string GetComponentPathPrefName( |
| const LanguagePackComponentConfig& config) { |
| return base::StrCat({kPrefNamePrefix, ToLanguageCode(config.language1), "_", |
| ToLanguageCode(config.language2), |
| kComponentPathPrefNameSuffix}); |
| } |
| |
| std::string GetRegisteredFlagPrefName( |
| const LanguagePackComponentConfig& config) { |
| return base::StrCat({kPrefNamePrefix, ToLanguageCode(config.language1), "_", |
| ToLanguageCode(config.language2), |
| kRegisteredFlagPrefNameSuffix}); |
| } |
| |
| // Returns the config for a language pack component. |
| const LanguagePackComponentConfig& GetLanguagePackComponentConfig( |
| LanguagePackKey key) { |
| return *kLanguagePackComponentConfigMap.at(key); |
| } |
| |
| // Calculates the required language packs for a translation from source_lang to |
| // target_lang. |
| // Note: Currently, this method is implemented assuming that translation between |
| // non-English languages is done by first translating to English. This logic |
| // needs to be updated when direct translation between non-English languages is |
| // supported by the library. |
| std::set<LanguagePackKey> CalculateRequiredLanguagePacks( |
| const std::string& source_lang, |
| const std::string& target_lang) { |
| auto source_lang_code = ToSupportedLanguage(source_lang); |
| auto target_lang_code = ToSupportedLanguage(target_lang); |
| if (!source_lang_code.has_value() || !target_lang_code.has_value() || |
| source_lang_code == target_lang_code) { |
| return {}; |
| } |
| if (*source_lang_code == SupportedLanguage::kEn) { |
| return {LanguagePackKeyFromNonEnglishSupportedLanguage(*target_lang_code)}; |
| } |
| if (*target_lang_code == SupportedLanguage::kEn) { |
| return {LanguagePackKeyFromNonEnglishSupportedLanguage(*source_lang_code)}; |
| } |
| return {LanguagePackKeyFromNonEnglishSupportedLanguage(*source_lang_code), |
| LanguagePackKeyFromNonEnglishSupportedLanguage(*target_lang_code)}; |
| } |
| |
| LanguagePackRequirements CalculateLanguagePackRequirements( |
| const std::string& source_lang, |
| const std::string& target_lang) { |
| LanguagePackRequirements language_pack_requirements; |
| |
| // Calculate required language packs. |
| language_pack_requirements.required_packs = |
| CalculateRequiredLanguagePacks(source_lang, target_lang); |
| |
| // Calculate required, not installed language packs. |
| const auto installed_packs = ComponentManager::GetInstalledLanguagePacks(); |
| std::ranges::set_difference( |
| language_pack_requirements.required_packs, installed_packs, |
| std::back_inserter( |
| language_pack_requirements.required_not_installed_packs)); |
| |
| // Calculate to be registered language packs. |
| const auto registered_packs = ComponentManager::GetRegisteredLanguagePacks(); |
| std::ranges::set_difference( |
| language_pack_requirements.required_not_installed_packs, registered_packs, |
| std::back_inserter(language_pack_requirements.to_be_registered_packs)); |
| |
| return language_pack_requirements; |
| } |
| |
| std::string GetPackageInstallDirName(LanguagePackKey language_pack_key) { |
| const auto supported_language = |
| NonEnglishSupportedLanguageFromLanguagePackKey(language_pack_key); |
| const auto lang_pair = |
| SupportedLanguagePairFromNonEnglishSupportedLanguage(supported_language); |
| return base::StrCat( |
| {ToLanguageCode(lang_pair.first), "_", ToLanguageCode(lang_pair.second)}); |
| } |
| |
| std::string GetPackageNameSuffix(LanguagePackKey language_pack_key) { |
| const auto supported_language = |
| NonEnglishSupportedLanguageFromLanguagePackKey(language_pack_key); |
| const auto lang_pair = |
| SupportedLanguagePairFromNonEnglishSupportedLanguage(supported_language); |
| return base::StrCat( |
| {ToLanguageCode(lang_pair.first), "-", ToLanguageCode(lang_pair.second)}); |
| } |
| |
| std::vector<std::string> GetPackageInstallSubDirNamesForVerification( |
| LanguagePackKey language_pack_key) { |
| const auto supported_language = |
| NonEnglishSupportedLanguageFromLanguagePackKey(language_pack_key); |
| const auto lang_pair = |
| SupportedLanguagePairFromNonEnglishSupportedLanguage(supported_language); |
| return { |
| base::StrCat({ToLanguageCode(lang_pair.first), "_", |
| ToLanguageCode(lang_pair.second), "_dictionary"}), |
| base::StrCat({ToLanguageCode(lang_pair.first), "_", |
| ToLanguageCode(lang_pair.second), "_nmt"}), |
| base::StrCat({ToLanguageCode(lang_pair.second), "_", |
| ToLanguageCode(lang_pair.first), "_nmt"}), |
| }; |
| } |
| |
| std::string_view GetSourceLanguageCode(LanguagePackKey language_pack_key) { |
| const SupportedLanguage supported_language = |
| NonEnglishSupportedLanguageFromLanguagePackKey(language_pack_key); |
| const auto [source_lang, _] = |
| SupportedLanguagePairFromNonEnglishSupportedLanguage(supported_language); |
| return ToLanguageCode(source_lang); |
| } |
| |
| std::string_view GetTargetLanguageCode(LanguagePackKey language_pack_key) { |
| const SupportedLanguage supported_language = |
| NonEnglishSupportedLanguageFromLanguagePackKey(language_pack_key); |
| const auto [_, target_lang] = |
| SupportedLanguagePairFromNonEnglishSupportedLanguage(supported_language); |
| return ToLanguageCode(target_lang); |
| } |
| |
| } // namespace on_device_translation |