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
Current section
221 Versions
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…