Packages

phoenix_kit

1.7.116
1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

221 Versions

Jump to

Compare versions

14 files changed
+368 additions
-93 deletions
unknownCHANGELOG.md
File is too large to be displayed (100 KB limit).
  @@ -1,6 +1,6 @@
1 1 {<<"links">>,[{<<"GitHub">>,<<"https://github.com/BeamLabEU/phoenix_kit">>}]}.
2 2 {<<"name">>,<<"phoenix_kit">>}.
3 - {<<"version">>,<<"1.7.115">>}.
3 + {<<"version">>,<<"1.7.116">>}.
4 4 {<<"description">>,
5 5 <<65,32,102,111,117,110,100,97,116,105,111,110,32,102,111,114,32,98,117,105,
6 6 108,100,105,110,103,32,69,108,105,120,105,114,32,80,104,111,101,110,105,
  @@ -89,6 +89,7 @@
89 89 <<"lib/modules/sitemap/web/controller.ex">>,
90 90 <<"lib/modules/sitemap/sitemap_file.ex">>,
91 91 <<"lib/modules/sitemap/html_generator.ex">>,
92 + <<"lib/modules/sitemap/locale_path.ex">>,
92 93 <<"lib/modules/sitemap/generator.ex">>,<<"lib/modules/sitemap/sitemap.ex">>,
93 94 <<"lib/modules/sitemap/route_resolver.ex">>,
94 95 <<"lib/modules/sitemap/sources">>,
  @@ -358,4 +358,40 @@ defmodule PhoenixKit.Modules.Languages.DialectMapper do
358 358 "pt-BR"
359 359 """
360 360 def default_dialects, do: @default_dialects
361 +
362 + @doc """
363 + Counts how many entries share each base language code in a list of
364 + language entries. Used by language switchers to decide whether to
365 + show a country qualifier or just the bare language name.
366 +
367 + Accepts both maps with atom-keyed `:code` (e.g. `%Language{}` structs)
368 + and string-keyed `"code"` (e.g. JSON-decoded settings). Entries
369 + without a recognizable code are skipped.
370 +
371 + ## Examples
372 +
373 + iex> DialectMapper.group_dialects_by_base([
374 + ...> %{code: "en-US"},
375 + ...> %{code: "en-GB"},
376 + ...> %{code: "et-EE"}
377 + ...> ])
378 + %{"en" => 2, "et" => 1}
379 +
380 + iex> DialectMapper.group_dialects_by_base([])
381 + %{}
382 + """
383 + def group_dialects_by_base(languages) when is_list(languages) do
384 + languages
385 + |> Enum.reduce([], fn lang, acc ->
386 + case lang_code(lang) do
387 + nil -> acc
388 + code -> [extract_base(code) | acc]
389 + end
390 + end)
391 + |> Enum.frequencies()
392 + end
393 +
394 + defp lang_code(%{code: code}) when is_binary(code), do: code
395 + defp lang_code(%{"code" => code}) when is_binary(code), do: code
396 + defp lang_code(_), do: nil
361 397 end
  @@ -577,6 +577,45 @@ defmodule PhoenixKit.Modules.Languages do
577 577 @spec default_language_no_prefix_key() :: String.t()
578 578 def default_language_no_prefix_key, do: @default_language_no_prefix_key
579 579
580 + @doc """
581 + Boot- and mix-task-safe wrapper around `default_language_no_prefix?/0`.
582 +
583 + URL-emission call sites (`PhoenixKit.Utils.Routes`,
584 + `PhoenixKitWeb.Users.Auth`) need the setting before the application
585 + is fully up — during `mix phoenix_kit.install`, during host-app
586 + routing macros that execute at compile time, or when the Settings
587 + table simply doesn't exist yet. This wrapper:
588 +
589 + * Returns `false` during mix-task context (detected via the same
590 + sentinel `Routes.path/1` uses) so admin URL emission stays
591 + sensible without DB.
592 + * Rescues any other exception (DB unreachable, schema mismatch,
593 + cache miss) to `false` — the safer default since prefixed URLs
594 + resolve under both setting states.
595 +
596 + Use `default_language_no_prefix?/0` from runtime contexts where the
597 + DB is guaranteed reachable. Use this from boot/middleware contexts.
598 + """
599 + @spec prefixless_primary_safe?() :: boolean()
600 + def prefixless_primary_safe? do
601 + if mix_task_context?() do
602 + false
603 + else
604 + default_language_no_prefix?()
605 + end
606 + rescue
607 + _ -> false
608 + end
609 +
610 + # Mirrors `PhoenixKit.Utils.Routes.mix_task_context?/0` — the
611 + # settings-cache-status sentinel is the same regardless of caller.
612 + defp mix_task_context? do
613 + case Process.get(:phoenix_kit_config_status) do
614 + nil -> false
615 + _ -> true
616 + end
617 + end
618 +
580 619 @doc """
581 620 Gets a specific language by its code.
  @@ -0,0 +1,63 @@
1 + defmodule PhoenixKit.Modules.Sitemap.LocalePath do
2 + @moduledoc """
3 + Shared locale-segment policy for sitemap sources.
4 +
5 + Every sitemap source (`publishing`, `static`, `posts`, ...) decides
6 + whether a URL entry should carry a language prefix using the same
7 + three rules:
8 +
9 + 1. No language supplied → no prefix.
10 + 2. Single-language mode (Languages module disabled or only one
11 + enabled) → no prefix is meaningful.
12 + 3. The language is the site's primary AND the site-wide
13 + `Languages.default_language_no_prefix?/0` setting is on → omit
14 + the prefix to keep the sitemap consistent with the public URL
15 + shape served at request time. Indexed canonicals must not drift
16 + from served URLs.
17 + 4. Otherwise → emit the prefix.
18 +
19 + Each source still decides **how** to format the prefix (base code via
20 + `DialectMapper.extract_base/1`, or display code via
21 + `LanguageHelpers.get_display_code/2` for hreflang-aware emission) —
22 + this module owns only the decision, not the formatting.
23 + """
24 +
25 + alias PhoenixKit.Modules.Languages
26 +
27 + @doc """
28 + Returns true when the sitemap entry for `language` should include
29 + the locale segment. `is_default` should be true when `language` is
30 + the site's primary language.
31 +
32 + Wrapped to survive boot / mix-task contexts where the Languages
33 + module or Settings table may not be reachable —
34 + `single_language_mode?/0`'s rescue returns `true` in that case, so
35 + `emit_prefix?/2` falls back to `false` (no prefix). That keeps the
36 + sitemap consistent with single-language sites under DB-unreachable
37 + conditions; runtime sitemap generation always has a reachable DB.
38 + """
39 + @spec emit_prefix?(String.t() | nil, boolean()) :: boolean()
40 + def emit_prefix?(nil, _is_default), do: false
41 +
42 + def emit_prefix?(_language, is_default) do
43 + cond do
44 + single_language_mode?() -> false
45 + is_default and Languages.default_language_no_prefix?() -> false
46 + true -> true
47 + end
48 + end
49 +
50 + @doc """
51 + Returns true when the site is effectively single-language (Languages
52 + module disabled, or only one language enabled). Defensive: rescues
53 + to `true` on any lookup failure so the sitemap omits prefixes
54 + during install / mix tasks rather than emitting broken multi-lang
55 + entries.
56 + """
57 + @spec single_language_mode?() :: boolean()
58 + def single_language_mode? do
59 + not Languages.enabled?() or length(Languages.get_enabled_languages()) <= 1
60 + rescue
61 + _ -> true
62 + end
63 + end
Loading more files…