Packages
phoenix_kit
1.7.115
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
9
files changed
+198
additions
-35
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.114">>}. |
| 3 | + {<<"version">>,<<"1.7.115">>}. |
| 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, |
| @@ -104,6 +104,7 @@ defmodule PhoenixKit.Modules.Languages do | |
| 104 104 | alias PhoenixKit.Settings |
| 105 105 | |
| 106 106 | @config_key "languages_config" |
| 107 | + @default_language_no_prefix_key "default_language_no_prefix" |
| 107 108 | @default_locale Config.default_locale() |
| 108 109 | @enabled_key "languages_enabled" |
| 109 110 | @module_name "languages" |
| @@ -258,6 +259,41 @@ defmodule PhoenixKit.Modules.Languages do | |
| 258 259 | :enable_failed -> :ok |
| 259 260 | end |
| 260 261 | |
| 262 | + @impl PhoenixKit.Module |
| 263 | + @doc """ |
| 264 | + Backfills the `default_language_no_prefix` setting from the legacy |
| 265 | + `publishing_default_language_no_prefix` key the first time this runs |
| 266 | + on an install that predates the site-wide setting. Idempotent — once |
| 267 | + the new key is set, subsequent calls are no-ops. |
| 268 | + |
| 269 | + Called by `PhoenixKit.ModuleRegistry.run_all_legacy_migrations/0` |
| 270 | + from the host app's `Application.start/2`. |
| 271 | + """ |
| 272 | + @spec migrate_legacy() :: :ok | {:ok, map()} |
| 273 | + def migrate_legacy do |
| 274 | + new_key = @default_language_no_prefix_key |
| 275 | + legacy_key = "publishing_default_language_no_prefix" |
| 276 | + |
| 277 | + case Settings.get_setting(new_key) do |
| 278 | + nil -> |
| 279 | + case Settings.get_setting(legacy_key) do |
| 280 | + nil -> |
| 281 | + {:ok, %{default_language_no_prefix: :not_migrated}} |
| 282 | + |
| 283 | + legacy_value when is_binary(legacy_value) -> |
| 284 | + # Settings serialize booleans as the strings "true"/"false"; |
| 285 | + # rewrite the same shape under the new key so |
| 286 | + # `get_boolean_setting/2` parses it identically. |
| 287 | + {:ok, _} = Settings.update_setting(new_key, legacy_value) |
| 288 | + |
| 289 | + {:ok, %{default_language_no_prefix: :migrated_from_legacy, value: legacy_value}} |
| 290 | + end |
| 291 | + |
| 292 | + _existing -> |
| 293 | + {:ok, %{default_language_no_prefix: :already_set}} |
| 294 | + end |
| 295 | + end |
| 296 | + |
| 261 297 | @impl PhoenixKit.Module |
| 262 298 | @doc """ |
| 263 299 | Checks if the language module is enabled. |
| @@ -500,6 +536,47 @@ defmodule PhoenixKit.Modules.Languages do | |
| 500 536 | end |
| 501 537 | end |
| 502 538 | |
| 539 | + @doc """ |
| 540 | + Returns true when public + admin URLs should omit the locale segment |
| 541 | + for the primary language. |
| 542 | + |
| 543 | + Site-wide setting (key `default_language_no_prefix`). When `true`, |
| 544 | + every URL generator that honors it — `Routes.path/1`, |
| 545 | + `Routes.admin_path/2`, the sitemap, publishing's HTML builders, the |
| 546 | + publishing canonical-redirect controller, and the publishing |
| 547 | + `RouterDispatch` rewriter — emits the primary language as the |
| 548 | + prefixless shape (`/admin/users`, `/blog/post`). When `false`, all |
| 549 | + generators emit the prefixed shape (`/en/admin/users`, |
| 550 | + `/en/blog/post`). |
| 551 | + |
| 552 | + Default: `false` (matches the historical publishing default and |
| 553 | + keeps existing installs' indexed URLs stable on upgrade). The |
| 554 | + setting can be migrated from the legacy |
| 555 | + `publishing_default_language_no_prefix` key — see `migrate_legacy/0`. |
| 556 | + """ |
| 557 | + @spec default_language_no_prefix?() :: boolean() |
| 558 | + def default_language_no_prefix? do |
| 559 | + Settings.get_boolean_setting(@default_language_no_prefix_key, false) |
| 560 | + end |
| 561 | + |
| 562 | + @doc """ |
| 563 | + Persists the `default_language_no_prefix` setting. Returns |
| 564 | + `{:ok, setting}` on success or `{:error, changeset}` on failure. |
| 565 | + """ |
| 566 | + @spec set_default_language_no_prefix(boolean()) :: |
| 567 | + {:ok, PhoenixKit.Settings.Setting.t()} | {:error, Ecto.Changeset.t()} |
| 568 | + def set_default_language_no_prefix(value) when is_boolean(value) do |
| 569 | + Settings.update_boolean_setting(@default_language_no_prefix_key, value) |
| 570 | + end |
| 571 | + |
| 572 | + @doc """ |
| 573 | + Returns the storage key used for the no-prefix setting. Exposed for |
| 574 | + the legacy-key migration in `PhoenixKit.Migration`; callers should |
| 575 | + prefer `default_language_no_prefix?/0`. |
| 576 | + """ |
| 577 | + @spec default_language_no_prefix_key() :: String.t() |
| 578 | + def default_language_no_prefix_key, do: @default_language_no_prefix_key |
| 579 | + |
| 503 580 | @doc """ |
| 504 581 | Gets a specific language by its code. |
| @@ -511,24 +511,30 @@ defmodule PhoenixKit.Modules.Sitemap.Sources.Publishing do | |
| 511 511 | |> Enum.map_join(" ", &String.capitalize/1) |
| 512 512 | end |
| 513 513 | |
| 514 | - # Build group path with PhoenixKit prefix and optional language |
| 514 | + # Build group path with PhoenixKit prefix and optional language. |
| 515 515 | # Format: /{prefix}/{lang?}/{segments...} |
| 516 | - # When in single language mode, no language prefix is added for anyone |
| 517 | - # When in multi-language mode, ALL languages get prefix (including default) |
| 518 | - defp build_group_path(segments, language, _is_default) do |
| 516 | + # |
| 517 | + # Skip rules for the language segment: |
| 518 | + # 1. Single-language mode — no language is meaningful. |
| 519 | + # 2. `is_default` is true AND the site-wide |
| 520 | + # `default_language_no_prefix` setting is on (the primary language |
| 521 | + # should match the public URL shape that |
| 522 | + # `PublishingHTML.use_language_prefix?/1` emits at request time; |
| 523 | + # keeping the sitemap consistent prevents indexed canonicals |
| 524 | + # drifting from served URLs). |
| 525 | + # Everything else keeps the prefix. |
| 526 | + defp build_group_path(segments, language, is_default) do |
| 519 527 | prefix_parts = url_prefix_segments() |
| 520 528 | |
| 521 | - # Add language prefix when: |
| 522 | - # 1. Language is specified |
| 523 | - # 2. Multiple languages are enabled (not single language mode) |
| 524 529 | lang_parts = |
| 525 | - if language && !single_language_mode?() do |
| 530 | + cond do |
| 531 | + is_nil(language) -> [] |
| 532 | + single_language_mode?() -> [] |
| 533 | + is_default and Languages.default_language_no_prefix?() -> [] |
| 526 534 | # Use display code to match controller's canonical URL logic |
| 527 535 | # This returns base code ("en") when single dialect enabled, |
| 528 536 | # or full code ("en-US") when multiple dialects enabled |
| 529 | - [get_display_code(language)] |
| 530 | - else |
| 531 | - [] |
| 537 | + true -> [get_display_code(language)] |
| 532 538 | end |
| 533 539 | |
| 534 540 | all_parts = |
| @@ -96,6 +96,16 @@ defmodule PhoenixKit.Settings.Setting do | |
| 96 96 | |
| 97 97 | @primary_key {:uuid, UUIDv7, autogenerate: true} |
| 98 98 | |
| 99 | + @type t :: %__MODULE__{ |
| 100 | + uuid: String.t() | nil, |
| 101 | + key: String.t() | nil, |
| 102 | + value: String.t() | nil, |
| 103 | + value_json: map() | nil, |
| 104 | + module: String.t() | nil, |
| 105 | + date_added: DateTime.t() | nil, |
| 106 | + date_updated: DateTime.t() | nil |
| 107 | + } |
| 108 | + |
| 99 109 | schema "phoenix_kit_settings" do |
| 100 110 | field :key, :string |
| 101 111 | field :value, :string |
Loading more files…