PhoenixKit.Modules.Languages.DialectMapper (phoenix_kit v1.7.115)

Copy Markdown View Source

Handles mapping between base language codes (en, es) and full dialect codes (en-US, es-MX).

This module provides the core logic for PhoenixKit's simplified URL architecture where URLs show base codes (/en/) but translations use full dialect codes (en-US).

Architecture

PhoenixKit uses a two-tier locale system:

  1. Base Language Codes - Used in URLs for simplicity

    • Format: 2-letter ISO 639-1 codes (en, es, fr, de, pt, zh, ja, etc.)
    • Examples: /en/dashboard, /es/admin, /fr/users
    • User-facing, SEO-friendly, easy to remember
  2. Full Dialect Codes - Used internally for translations

    • Format: BCP 47 language tags (en-US, es-MX, pt-BR, zh-CN)
    • Examples: en-US, en-GB, es-ES, es-MX, pt-PT, pt-BR
    • Translation-aware, respects regional differences

Data Flow

User visits: /en/dashboard
      
Extract base: "en"
      
Resolve dialect: "en"  "en-US" (default mapping for the base code)
      
Set Gettext: "en-US"
      
Generate URLs: Always use base code "en"

Default Dialect Mapping

When no user preference exists, base codes map to most common regional variants:

  • enen-US (American English)
  • eses-ES (European Spanish)
  • ptpt-BR (Brazilian Portuguese)
  • zhzh-CN (Simplified Chinese)
  • dede-DE (German Germany)
  • frfr-FR (French France)

URL-Driven Resolution

Dialect resolution is purely URL-driven: a base code maps to its default dialect and nothing else. A logged-in user's locale preference is intentionally not consulted (see PhoenixKitWeb.Users.Auth for the URL-is-authoritative rationale), so /en/dashboard always resolves to the default en-US regardless of who is signed in.

Examples

# Extract base language from full dialect
iex> DialectMapper.extract_base("en-US")
"en"

iex> DialectMapper.extract_base("es-MX")
"es"

# Convert base to default dialect
iex> DialectMapper.base_to_dialect("en")
"en-US"

iex> DialectMapper.base_to_dialect("pt")
"pt-BR"

# Resolve a base code to its default dialect (URL-driven)
iex> DialectMapper.resolve_dialect("en")
"en-US"

Validation

iex> DialectMapper.valid_base_code?("en")
true

iex> DialectMapper.valid_base_code?("xx")
false

Getting Available Dialects

iex> DialectMapper.dialects_for_base("en")
["en-US", "en-GB", "en-CA", "en-AU"]

iex> DialectMapper.dialects_for_base("es")
["es-ES", "es-MX", "es-AR", "es-CO"]

Summary

Functions

Converts base language code to default dialect.

Gets the default dialects map.

Gets all available dialect codes for a base language.

Extracts base language code from full dialect code.

Resolves the full dialect code for a base language URL.

Validates if a base language code is supported.

Functions

base_to_dialect(base_code)

Converts base language code to default dialect.

Uses predefined mapping for most common regional variants. Falls back to base code if no mapping exists.

Examples

iex> DialectMapper.base_to_dialect("en")
"en-US"

iex> DialectMapper.base_to_dialect("pt")
"pt-BR"

iex> DialectMapper.base_to_dialect("ja")
"ja"

iex> DialectMapper.base_to_dialect("xx")
"xx"

default_dialects()

Gets the default dialects map.

Useful for debugging, testing, or documentation purposes.

Examples

iex> defaults = DialectMapper.default_dialects()
iex> defaults["en"]
"en-US"

iex> defaults["pt"]
"pt-BR"

dialects_for_base(base_code)

Gets all available dialect codes for a base language.

Searches the predefined language list for all dialects matching the given base code.

Examples

iex> DialectMapper.dialects_for_base("en")
["en-US", "en-GB", "en-CA", "en-AU"]

iex> DialectMapper.dialects_for_base("es")
["es-ES", "es-MX", "es-AR", "es-CO"]

iex> DialectMapper.dialects_for_base("ja")
["ja"]

iex> DialectMapper.dialects_for_base("xx")
[]

Use Cases

  • Populate user preference dropdown
  • Admin analytics (dialects per base language)
  • Migration tools (find affected users)

extract_base(locale)

Extracts base language code from full dialect code.

Splits on hyphen and returns first part (lowercased). Handles both dialect codes (en-US) and base codes (en). Returns "en" as default fallback for nil and empty string values.

Examples

iex> DialectMapper.extract_base("en-US")
"en"

iex> DialectMapper.extract_base("es-MX")
"es"

iex> DialectMapper.extract_base("zh-Hans-CN")
"zh"

iex> DialectMapper.extract_base("ja")
"ja"

iex> DialectMapper.extract_base("EN-GB")
"en"

iex> DialectMapper.extract_base(nil)
"en"

iex> DialectMapper.extract_base("")
"en"

resolve_dialect(base_code)

Resolves the full dialect code for a base language URL.

The URL is authoritative: the base code maps straight to its default dialect via base_to_dialect/1. User locale preferences are deliberately NOT consulted here — locale resolution is URL-driven across both the LiveView mount and the HTTP plug (see the rationale in PhoenixKitWeb.Users.Auth). Resolving without a user keeps a logged-in user's custom_fields["preferred_locale"] from silently upgrading e.g. base "en" to "en-GB".

Examples

iex> DialectMapper.resolve_dialect("en")
"en-US"

iex> DialectMapper.resolve_dialect("es")
"es-ES"

iex> DialectMapper.resolve_dialect("ja")
"ja"

valid_base_code?(base_code)

Validates if a base language code is supported.

Checks if the default dialect for this base code exists in the predefined language list.

Examples

iex> DialectMapper.valid_base_code?("en")
true

iex> DialectMapper.valid_base_code?("ja")
true

iex> DialectMapper.valid_base_code?("xx")
false

iex> DialectMapper.valid_base_code?("en-US")
false  # Not a base code (contains hyphen)

Notes

  • Only validates base codes (2 letters)
  • Full dialect codes will return false (use extract_base first)
  • Checks against Languages.get_predefined_language/1