Packages

phoenix_kit

1.7.114
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

12 files changed
+411 additions
-423 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.113">>}.
3 + {<<"version">>,<<"1.7.114">>}.
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,
  @@ -103,7 +103,7 @@ alias PhoenixKit.Modules.Languages.DialectMapper
103 103
104 104 DialectMapper.extract_base("en-US") # => "en"
105 105 DialectMapper.base_to_dialect("en") # => "en-US"
106 - DialectMapper.resolve_dialect("en", user) # Considers user.custom_fields["preferred_locale"]
106 + DialectMapper.resolve_dialect("en") # => "en-US" (URL-driven; base -> default dialect)
107 107 ```
108 108
109 109 ## Key API Functions
  @@ -26,9 +26,9 @@ defmodule PhoenixKit.Modules.Languages.DialectMapper do
26 26 ↓
27 27 Extract base: "en"
28 28 ↓
29 - Resolve dialect: "en-US" (default) or user.custom_fields["preferred_locale"] ("en-GB")
29 + Resolve dialect: "en" → "en-US" (default mapping for the base code)
30 30 ↓
31 - Set Gettext: "en-US" or "en-GB"
31 + Set Gettext: "en-US"
32 32 ↓
33 33 Generate URLs: Always use base code "en"
34 34 ```
  @@ -43,13 +43,13 @@ defmodule PhoenixKit.Modules.Languages.DialectMapper do
43 43 - `de` → `de-DE` (German Germany)
44 44 - `fr` → `fr-FR` (French France)
45 45
46 - ## User Preferences
46 + ## URL-Driven Resolution
47 47
48 - Authenticated users can override default mappings:
49 - - User prefers British English: sets `custom_fields["preferred_locale"]` = "en-GB"
50 - - Visits `/en/dashboard`
51 - - System uses "en-GB" for translations
52 - - URLs remain `/en/` (not `/en-GB/`)
48 + Dialect resolution is purely URL-driven: a base code maps to its
49 + default dialect and nothing else. A logged-in user's locale preference
50 + is intentionally not consulted (see `PhoenixKitWeb.Users.Auth` for the
51 + URL-is-authoritative rationale), so `/en/dashboard` always resolves to
52 + the default `en-US` regardless of who is signed in.
53 53
54 54 ## Examples
55 55
  @@ -67,12 +67,8 @@ defmodule PhoenixKit.Modules.Languages.DialectMapper do
67 67 iex> DialectMapper.base_to_dialect("pt")
68 68 "pt-BR"
69 69
70 - # Resolve dialect with user preference (stored in custom_fields)
71 - iex> user = %User{custom_fields: %{"preferred_locale" => "en-GB"}}
72 - iex> DialectMapper.resolve_dialect("en", user)
73 - "en-GB"
74 -
75 - iex> DialectMapper.resolve_dialect("en", nil)
70 + # Resolve a base code to its default dialect (URL-driven)
71 + iex> DialectMapper.resolve_dialect("en")
76 72 "en-US"
77 73
78 74 ## Validation
  @@ -249,53 +245,28 @@ defmodule PhoenixKit.Modules.Languages.DialectMapper do
249 245 end
250 246
251 247 @doc """
252 - Resolves the full dialect code for a user visiting a base language URL.
248 + Resolves the full dialect code for a base language URL.
253 249
254 - Resolution priority:
255 - 1. User's saved preference (if authenticated and preference matches base code)
256 - 2. Default dialect mapping for that base language
250 + The URL is authoritative: the base code maps straight to its default
251 + dialect via `base_to_dialect/1`. User locale preferences are
252 + deliberately NOT consulted here — locale resolution is URL-driven
253 + across both the LiveView mount and the HTTP plug (see the rationale in
254 + `PhoenixKitWeb.Users.Auth`). Resolving without a user keeps a logged-in
255 + user's `custom_fields["preferred_locale"]` from silently upgrading e.g.
256 + base `"en"` to `"en-GB"`.
257 257
258 258 ## Examples
259 259
260 - iex> user = %User{custom_fields: %{"preferred_locale" => "en-GB"}}
261 - iex> DialectMapper.resolve_dialect("en", user)
262 - "en-GB"
263 -
264 - iex> user = %User{custom_fields: %{"preferred_locale" => "es-MX"}}
265 - iex> DialectMapper.resolve_dialect("en", user)
266 - "en-US" # Preference doesn't match base, use default
267 -
268 - iex> DialectMapper.resolve_dialect("en", nil)
260 + iex> DialectMapper.resolve_dialect("en")
269 261 "en-US"
270 262
271 - iex> guest = %{some_field: "value"}
272 - iex> DialectMapper.resolve_dialect("es", guest)
263 + iex> DialectMapper.resolve_dialect("es")
273 264 "es-ES"
274 265
275 - ## Security
276 -
277 - User preference only applied if it matches the requested base code.
278 - This prevents users from forcing unintended locales via preference tampering.
279 -
280 - ## Graceful Degradation
281 -
282 - If user preference becomes invalid (dialect disabled, typo, etc.),
283 - system falls back to default mapping. No crashes or errors.
266 + iex> DialectMapper.resolve_dialect("ja")
267 + "ja"
284 268 """
285 - def resolve_dialect(base_code, user \\ nil)
286 -
287 - def resolve_dialect(base_code, %{custom_fields: %{"preferred_locale" => preferred}} = _user)
288 - when is_binary(preferred) do
289 - # Verify user's preference matches the base code in URL
290 - # Security: prevents locale preference injection attacks
291 - if extract_base(preferred) == String.downcase(base_code) do
292 - preferred
293 - else
294 - base_to_dialect(base_code)
295 - end
296 - end
297 -
298 - def resolve_dialect(base_code, _user) do
269 + def resolve_dialect(base_code) when is_binary(base_code) do
299 270 base_to_dialect(base_code)
300 271 end
  @@ -354,55 +354,6 @@ defmodule PhoenixKit.Users.Auth.User do
354 354 change(user, confirmed_at: nil)
355 355 end
356 356
357 - @doc """
358 - A user changeset for updating preferred locale/dialect.
359 -
360 - This allows authenticated users to select their preferred dialect variant
361 - (e.g., en-GB instead of en-US) while URLs continue to show base codes.
362 - The locale is stored in the `custom_fields` JSONB column.
363 -
364 - ## Validation
365 -
366 - - Format: Must match ~r/^[a-z]{2,3}(-[A-Za-z]{2,4})?$/
367 - - Existence: Must exist in predefined language list
368 - - NULL/empty allowed: Indicates "use system default"
369 -
370 - ## Examples
371 -
372 - iex> preferred_locale_changeset(user, %{preferred_locale: "en-GB"})
373 - #Ecto.Changeset<...>
374 -
375 - iex> preferred_locale_changeset(user, %{preferred_locale: nil})
376 - #Ecto.Changeset<...> # Clears preference, uses defaults
377 -
378 - iex> preferred_locale_changeset(user, %{preferred_locale: "invalid"})
379 - #Ecto.Changeset<errors: [preferred_locale: {"must be a valid locale format", []}]>
380 - """
381 - def preferred_locale_changeset(user, attrs) do
382 - locale = Map.get(attrs, :preferred_locale) || Map.get(attrs, "preferred_locale")
383 -
384 - case validate_locale_value(locale) do
385 - :ok ->
386 - # Merge locale into custom_fields
387 - current_fields = user.custom_fields || %{}
388 -
389 - updated_fields =
390 - if locale && locale != "" do
391 - Map.put(current_fields, "preferred_locale", locale)
392 - else
393 - Map.delete(current_fields, "preferred_locale")
394 - end
395 -
396 - user
397 - |> change(custom_fields: updated_fields)
398 -
399 - {:error, message} ->
400 - user
401 - |> change()
402 - |> add_error(:preferred_locale, message)
403 - end
404 - end
405 -
406 357 @doc """
407 358 Validates a locale value for format and existence.
408 359
  @@ -434,25 +385,6 @@ defmodule PhoenixKit.Users.Auth.User do
434 385
435 386 def validate_locale_value(_), do: {:error, "must be a string"}
436 387
437 - @doc """
438 - Gets the user's preferred locale from custom_fields.
439 -
440 - Returns nil if not set (indicating system default should be used).
441 -
442 - ## Examples
443 -
444 - iex> get_preferred_locale(%User{custom_fields: %{"preferred_locale" => "en-GB"}})
445 - "en-GB"
446 -
447 - iex> get_preferred_locale(%User{custom_fields: %{}})
448 - nil
449 - """
450 - def get_preferred_locale(%__MODULE__{custom_fields: fields}) when is_map(fields) do
451 - Map.get(fields, "preferred_locale")
452 - end
453 -
454 - def get_preferred_locale(%__MODULE__{}), do: nil
455 -
456 388 @doc """
457 389 Verifies the password.
Loading more files…