lib/fermo/i18n.ex

defmodule Fermo.I18n do
  @callback load() :: {:ok}
  @callback load(String.t()) :: {:ok}
  def load(path \\ "priv/locales/**/*.yml") do
    files = Path.wildcard(path)
    translations = Enum.reduce(files, %{}, fn (file, translations) ->
      content = YamlElixir.read_from_file(file)
      {:ok, atom_keys} = Morphix.atomorphiform(content)
      DeepMerge.deep_merge(translations, atom_keys)
    end)
    {:ok} = I18n.put(translations)
  end

  @doc """
  Cross references all config pages, finding those with the same `:id`
  but with different locales.

  Each page with an `:id` and `:locale` in its parameters has
  a `:localized_paths` Map added indicating which locale has
  which path.

  E.g.

     %{
       path: "/",
       template: "home.html.slim",
       localized_paths: %{
         en: "/",
         it: "/it/"
       },
       params: %{
         id: "home_page",
         locale: :en
       }
     }

  In templates, this data is available via `context.page.localized_paths`.
  """
  @callback optionally_build_path_map(map()) :: map()
  def optionally_build_path_map(%{i18n: nil} = config), do: config
  def optionally_build_path_map(%{localized_paths: true, i18n: _i18n} = config) do
    pages_with_locale_and_id = Enum.filter(
      config.pages,
      fn %{params: params} ->
        Map.has_key?(params, :locale) && Map.has_key?(params, :id)
      end
    )

    path_locale_id = Enum.map(
      pages_with_locale_and_id,
      fn page ->
        %{path: page.path, id: page.params.id, locale: atom(page.params.locale)}
      end
    )

    by_id = Enum.group_by(path_locale_id, &(&1.id))

    path_map = Enum.into(
      by_id,
      %{},
      fn {id, pages} ->
        {
          id,
          Enum.into(pages, %{}, fn item -> {item.locale, item.path} end)
        }
      end
    )

    pages = Enum.map(
      config.pages,
      fn %{params: params} = page ->
        if Map.has_key?(params, :locale) && Map.has_key?(params, :id) do
          map = path_map[params.id]
          put_in(page, [:localized_paths], map)
        else
          page
        end
      end
    )

    config
    |> put_in([:pages], pages)
    |> put_in([:stats, :optionally_build_path_map_completed], Time.utc_now)
  end
  def optionally_build_path_map(%{path_map: true} = config) do
    IO.warn """
    `config.path_map` is deprecated.

    Set `config.localized_paths: true` in order to get a per-page Map
    of localized pages.
    """
    optionally_build_path_map(Map.put(config, :localized_paths, true))
  end
  def optionally_build_path_map(config), do: config

  def root_locale(config) do
    no_root_locale = Map.get(config, :no_root_locale, false)
    if no_root_locale do
      nil
    else
      default_locale = default_locale(config)
      if default_locale do
        default_locale
      else
        first_locale(config)
      end
    end
  end

  def default_locale(%{default_locale: default_locale}), do: default_locale
  def default_locale(_), do: nil

  def first_locale(%{i18n: []}), do: nil
  def first_locale(%{i18n: [locale | _rest]}), do: locale
  def first_locale(_config), do: nil

  defp atom(x) when is_atom(x), do: x
  defp atom(x), do: String.to_atom(x)
end