lib/desktop.ex

defmodule Desktop do
  @moduledoc """
  This is the documentation for the Desktop project.

  By default, Desktop applications depend on the following packages:

    * [Phoenix](https://hexdocs.pm/phoenix) - the Phoenix web framework

    * [Phoenix LiveView](https://hexdocs.pm/phoenix_live_view) - real-time
      user experience

    * [Sqlite3 Ecto](https://github.com/elixir-sqlite/ecto_sqlite3) - local
      database

  To get started, see the [Getting Started Guide](./guides/getting_started.md).

  This library is still worked on heaviliy and APIs are going to change in v2. If you are curious
  to play around or contribute it's to best clone the current sample app at:
  https://github.com/elixir-desktop/desktop-example-app and start modifying it to your wishes.

  """
  use Application

  @doc false
  def start(:normal, []) do
    child = %{
      id: Desktop.Env,
      start: {Desktop.Env, :start_link, []}
    }

    Supervisor.start_link([child], strategy: :one_for_one, name: Desktop.Sup)
  end

  @doc false
  @spec start_link() :: :ignore | {:error, any} | {:ok, pid}
  def start_link() do
    GenServer.start_link(__MODULE__, [], name: __MODULE__)
  end

  @doc """
    Call on application to use the system locale by default for
    translations. The function will scan your gettext backend for available
    languages and compare to the system language to select the best match.

    ```
      Desktop.identify_default_locale(MyApp.Gettext)
    ```
  """
  def identify_default_locale(backend) do
    # this is in the form "xx_XX"
    language_code = language_code()

    # All locales with translations:
    known_locales = Gettext.known_locales(backend)

    # Looking for a best fit
    # Preferring a full match 'xx_xx' == 'yy_yy'
    best_match = Enum.find(known_locales, fn l -> String.downcase(l) == language_code end)

    if best_match != nil do
      put_default_locale(best_match)
    else
      # Looking for a prefix match 'xx' == 'yy'
      prefix = binary_part(language_code, 0, 2)

      prefix_match =
        Enum.find(known_locales, fn l -> String.starts_with?(String.downcase(l), prefix) end)

      if prefix_match != nil do
        put_default_locale(prefix_match)
      else
        # we're giving up, not updating the default locale
      end
    end
  end

  def language_code() do
    with MacOS <- Desktop.OS.type(),
         {locale, 0} <- System.cmd("defaults", ~w(read -g AppleLocale)),
         [code] <- Regex.run(~r/[a-z]+_[A-Z]+/, locale) do
      # On MacOS getSystemLanguage() is calculated based on the the number
      # format (so mostly English) and not based on the actual used language
      # https://trac.wxwidgets.org/ticket/11594

      # So we are using this workaround instead to get the current locale as "xx_XX"
      # https://stackoverflow.com/questions/661935/how-to-detect-current-locale-in-mac-os-x-from-the-shell
      code
    else
      _ ->
        :wx.set_env(Desktop.Env.wx_env())
        locale = :wxLocale.new(:wxLocale.getSystemLanguage())
        # this is in the form "xx_XX"
        :wxLocale.getCanonicalName(locale) |> List.to_string() |> String.downcase()
    end
  end

  @doc """
    Allows setting the default locale that will be used for translations in this
    Desktop application.
  """
  def put_default_locale(locale) when is_binary(locale) do
    # :persistent_term.put(@key, locale)
    Application.put_env(:gettext, :default_locale, locale)
  end
end