chromium/chrome/updater/win/ui/l10n_util.cc

// Copyright 2022 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/updater/win/ui/l10n_util.h"

#include <string>
#include <vector>

#include "base/debug/alias.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/atl.h"
#include "base/win/embedded_i18n/language_selector.h"
#include "base/win/i18n.h"
#include "chrome/updater/util/util.h"
#include "chrome/updater/util/win_util.h"
#include "chrome/updater/win/installer/exit_code.h"
#include "chrome/updater/win/ui/resources/updater_installer_strings.h"

namespace updater {
namespace {

constexpr base::win::i18n::LanguageSelector::LangToOffset
    kLanguageOffsetPairs[] = {
#define HANDLE_LANGUAGE(l_, o_) {L## #l_, o_},
        DO_LANGUAGES
#undef HANDLE_LANGUAGE
};

size_t GetLanguageOffset(const std::wstring& lang) {
  return base::win::i18n::LanguageSelector(lang, kLanguageOffsetPairs).offset();
}

}  // namespace

std::wstring GetPreferredLanguage() {
  std::vector<std::wstring> languages;
  if (!base::win::i18n::GetUserPreferredUILanguageList(&languages) ||
      languages.size() == 0) {
    return L"en-us";
  }

  return languages[0];
}

std::wstring GetLocalizedString(unsigned int base_message_id,
                                const std::wstring& lang) {
  // Map `base_message_id` to the base id for the current install mode.
  const unsigned int message_id =
      static_cast<UINT>(base_message_id + GetLanguageOffset(lang));
  const ATLSTRINGRESOURCEIMAGE* image =
      AtlGetStringResourceImage(_AtlBaseModule.GetModuleInstance(), message_id);
  if (image) {
    return std::wstring(image->achString, image->nLength);
  }
  const DWORD error_code = ::GetLastError();
  base::debug::Alias(&base_message_id);
  base::debug::Alias(&message_id);
  base::debug::Alias(&error_code);
  DEBUG_ALIAS_FOR_CSTR(dbg_lang, base::WideToUTF8(lang).c_str(), 16);
  VLOG(2) << base_message_id << ", " << message_id << ", " << error_code << ", "
          << lang;
  NOTREACHED_IN_MIGRATION()
      << "Unable to find resource, " << base_message_id << ", " << message_id
      << ", " << error_code << ", " << lang;
  return std::wstring();
}

std::wstring GetLocalizedStringF(unsigned int base_message_id,
                                 const std::wstring& replacement,
                                 const std::wstring& lang) {
  return GetLocalizedStringF(base_message_id,
                             std::vector<std::wstring>{replacement}, lang);
}

std::wstring GetLocalizedStringF(unsigned int base_message_id,
                                 std::vector<std::wstring> replacements,
                                 const std::wstring& lang) {
  // Replacements start at index 1 because the implementation of
  // ReplaceStringPlaceholders does i+1, so the first placeholder would be `$1`.
  // A `$0` is considered an invalid placeholder.
  replacements.insert(replacements.begin(), {});
  return base::ReplaceStringPlaceholders(
      GetLocalizedString(base_message_id, lang), replacements, nullptr);
}

std::wstring GetLocalizedMetainstallerErrorString(DWORD exit_code,
                                                  DWORD windows_error) {
#define METAINSTALLER_ERROR_SWITCH_ENTRY(exit_code)                        \
  case static_cast<int>(exit_code):                                        \
    return GetLocalizedStringF(                                            \
        IDS_GENERIC_METAINSTALLER_ERROR_BASE,                              \
        {L#exit_code, windows_error ? GetTextForSystemError(windows_error) \
                                    : std::wstring()})

  switch (exit_code) {
    METAINSTALLER_ERROR_SWITCH_ENTRY(TEMP_DIR_FAILED);
    METAINSTALLER_ERROR_SWITCH_ENTRY(UNPACKING_FAILED);
    METAINSTALLER_ERROR_SWITCH_ENTRY(GENERIC_INITIALIZATION_FAILURE);
    METAINSTALLER_ERROR_SWITCH_ENTRY(COMMAND_STRING_OVERFLOW);
    METAINSTALLER_ERROR_SWITCH_ENTRY(WAIT_FOR_PROCESS_FAILED);
    METAINSTALLER_ERROR_SWITCH_ENTRY(PATH_STRING_OVERFLOW);
    METAINSTALLER_ERROR_SWITCH_ENTRY(UNABLE_TO_GET_WORK_DIRECTORY);
    METAINSTALLER_ERROR_SWITCH_ENTRY(UNABLE_TO_EXTRACT_ARCHIVE);
    METAINSTALLER_ERROR_SWITCH_ENTRY(UNEXPECTED_ELEVATION_LOOP);
    METAINSTALLER_ERROR_SWITCH_ENTRY(UNEXPECTED_DE_ELEVATION_LOOP);
    METAINSTALLER_ERROR_SWITCH_ENTRY(UNEXPECTED_ELEVATION_LOOP_SILENT);
    METAINSTALLER_ERROR_SWITCH_ENTRY(UNABLE_TO_SET_DIRECTORY_ACL);
    METAINSTALLER_ERROR_SWITCH_ENTRY(INVALID_OPTION);
    METAINSTALLER_ERROR_SWITCH_ENTRY(FAILED_TO_DE_ELEVATE_METAINSTALLER);
    METAINSTALLER_ERROR_SWITCH_ENTRY(RUN_SETUP_FAILED_FILE_NOT_FOUND);
    METAINSTALLER_ERROR_SWITCH_ENTRY(RUN_SETUP_FAILED_PATH_NOT_FOUND);
    METAINSTALLER_ERROR_SWITCH_ENTRY(RUN_SETUP_FAILED_COULD_NOT_CREATE_PROCESS);
    METAINSTALLER_ERROR_SWITCH_ENTRY(UNABLE_TO_GET_EXE_PATH);

    case UNSUPPORTED_WINDOWS_VERSION:
      return GetLocalizedString(IDS_UPDATER_OS_NOT_SUPPORTED_BASE);
    case FAILED_TO_ELEVATE_METAINSTALLER:
      return GetLocalizedStringF(IDS_FAILED_TO_ELEVATE_METAINSTALLER_BASE,
                                 GetTextForSystemError(windows_error));
    case UPDATER_EXIT_CODE:
    default:
      NOTREACHED_IN_MIGRATION();
      return {};
  }
#undef METAINSTALLER_ERROR_SWITCH_ENTRY
}

}  // namespace updater