# `PhoenixKit.Modules.Languages`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.87/lib/modules/languages/languages.ex#L1)

Languages management for PhoenixKit - complete language configuration in a single module.

This module provides management for language module in PhoenixKit applications.
It handles language configuration, settings, and language data through JSON settings.

## Language Structure

Each language has the following structure:
- `code`: Language code (e.g., "en-US", "es-ES", "fr-FR")
- `name`: Full language name (e.g., "English (United States)", "Spanish (Spain)", "French (France)")
- `is_default`: Boolean indicating if this is the default language
- `is_enabled`: Boolean indicating if this language is active

## Core Functions

### Languages Management
- `enabled?/0` - Check if languages are enabled
- `enable_system/0` - Enable languages with default English
- `disable_system/0` - Disable languages
- `get_config/0` - Get complete configuration

### Language Management
- `get_languages/0` - Get all configured languages
- `get_enabled_languages/0` - Get only enabled languages
- `get_default_language/0` - Get the default language
- `get_language/1` - Get a specific language by code
- `get_language_codes/0` - Get list of all language codes
- `get_enabled_language_codes/0` - Get list of enabled language codes
- `valid_language?/1` - Check if a language code exists
- `language_enabled?/1` - Check if a language is enabled
- `add_language/1` - Add a new language to the system
- `update_language/2` - Update an existing language
- `remove_language/1` - Remove a language from the system
- `set_default_language/1` - Set a new default language
- `enable_language/1` - Enable a specific language
- `disable_language/1` - Disable a specific language

## Usage Examples

    # Check if languages are enabled
    if PhoenixKit.Modules.Languages.enabled?() do
      # Languages are active
    end

    # Enable languages (creates default English language)
    {:ok, config} = PhoenixKit.Modules.Languages.enable_system()

    # Add a new language
    {:ok, config} = PhoenixKit.Modules.Languages.add_language("es")

    # Get all languages
    languages = PhoenixKit.Modules.Languages.get_languages()
    # => [%Language{code: "en-US", name: "English (United States)", is_default: true, is_enabled: true}, ...]

    # Get only enabled languages (most common use case)
    enabled_languages = PhoenixKit.Modules.Languages.get_enabled_languages()
    # => [%Language{code: "en-US", name: "English (United States)", ...}, ...]

    # Get a specific language by code
    spanish = PhoenixKit.Modules.Languages.get_language("es-ES")
    # => %{code: "es-ES", name: "Spanish (Spain)", is_enabled: true}

    # Get just the language codes
    codes = PhoenixKit.Modules.Languages.get_enabled_language_codes()
    # => ["en-US", "es-ES", "fr-FR"]

    # Check if a language is valid and enabled
    if PhoenixKit.Modules.Languages.language_enabled?("es-ES") do
      # Use Spanish language
    end

## JSON Storage Format

Languages are stored in the `languages_config` setting as JSON.
The array order determines the display order:

    {
      "languages": [
        {
          "code": "en-US",
          "name": "English (United States)",
          "is_default": true,
          "is_enabled": true
        },
        {
          "code": "es-ES",
          "name": "Spanish (Spain)",
          "is_default": false,
          "is_enabled": true
        }
      ]
    }

# `add_language`

Adds a predefined language to the module by language code.

Takes a language code and adds the corresponding predefined language
to the module configuration. Only languages from the predefined list
can be added.

## Examples

    iex> PhoenixKit.Modules.Languages.add_language("es-ES")
    {:ok, updated_config}

    iex> PhoenixKit.Modules.Languages.add_language("en-US")  # if already exists
    {:error, "Language already exists"}

    iex> PhoenixKit.Modules.Languages.add_language("invalid")
    {:error, "Language not found in available languages"}

# `disable_language`

Disables a specific language.

Cannot disable the default language.

## Examples

    iex> PhoenixKit.Modules.Languages.disable_language("es-ES")
    {:ok, updated_config}

    iex> PhoenixKit.Modules.Languages.disable_language("en-US")  # if it's default
    {:error, "Cannot disable default language"}

# `disable_system`

Disables the language module.

Turns off the language module but preserves the language configuration.

Returns `{:ok, setting}` on success, `{:error, changeset}` on failure.

## Examples

    iex> PhoenixKit.Modules.Languages.disable_system()
    {:ok, %Setting{}}

# `enable_language`

Enables a specific language.

## Examples

    iex> PhoenixKit.Modules.Languages.enable_language("es-ES")
    {:ok, updated_config}

# `enable_system`

Enables the language module and creates default configuration.

Creates the initial module configuration with English as the default language.
If a previous configuration exists, it will be restored instead of reset.
Updates both the enabled flag and the JSON configuration.

Returns `{:ok, config}` on success, `{:error, reason}` on failure.

## Examples

    iex> PhoenixKit.Modules.Languages.enable_system()
    {:ok, %{"languages" => [%{"code" => "en-US", ...}]}}

# `enabled?`

Checks if the language module is enabled.

Returns true if the module is enabled, false otherwise.

## Examples

    iex> PhoenixKit.Modules.Languages.enabled?()
    false

# `enabled_locale_codes`

Gets enabled language codes for locale-based routing.

Returns a list of enabled language codes that can be used in URL routing.
Falls back to ["en"] when the language module is disabled.

## Examples

    iex> PhoenixKit.Modules.Languages.enabled_locale_codes()
    ["en-US", "es-ES", "fr-FR"]

    # When system is disabled:
    iex> PhoenixKit.Modules.Languages.enabled_locale_codes()
    ["en-US"]

# `get_available_language_by_code`

Gets a language by code with O(1) lookup from cached map.
Returns nil if not found.

# `get_available_languages`

Gets all available predefined languages.

Returns the complete list of languages that can be added to the system.
This list is used to populate dropdown selections.

## Examples

    iex> PhoenixKit.Modules.Languages.get_available_languages()
    [%{code: "en-US", name: "English (United States)", native: "English (US)", flag: "🇺🇸"}, ...]

# `get_available_languages_for_selection`

Gets available languages for selection (excludes already added languages).

Returns languages that can be added to the system, filtered to exclude
languages that have already been configured.

## Examples

    iex> PhoenixKit.Modules.Languages.get_available_languages_for_selection()
    [%{code: "es-ES", name: "Spanish (Spain)", native: "Español (España)", flag: "🇪🇸"}, ...]

# `get_config`

Gets the complete language module configuration.

Returns a map with module status and language configuration.

## Examples

    iex> PhoenixKit.Modules.Languages.get_config()
    %{
      enabled: true,
      languages: [%{"code" => "en-US", "name" => "English (United States)", ...}],
      language_count: 1,
      enabled_count: 1,
      default_language: %{"code" => "en-US", "name" => "English (United States)", ...}
    }

# `get_default_language`

Gets the default language.

Returns the language map marked as default, or nil if none found.

## Examples

    iex> PhoenixKit.Modules.Languages.get_default_language()
    %{"code" => "en-US", "name" => "English (United States)", "is_default" => true, ...}

    # When system is disabled:
    iex> PhoenixKit.Modules.Languages.get_default_language()
    nil

# `get_default_language_codes`

Gets the default popular languages for admin panel display.

Returns a list of the most commonly used language codes that should
be available in the admin panel language selector.

## Examples

    iex> PhoenixKit.Modules.Languages.get_default_language_codes()
    ["en-US", "es-ES", "fr-FR", "de-DE", "pt-BR", "it", "nl", "ru", "ja", "ko", "zh", "ar", "et"]

# `get_display_languages`

Gets the appropriate language list for frontend display.

Returns the configured languages if the module is enabled.
Otherwise, returns the default languages for display.

This allows the frontend to always show a language list, reverting to defaults when
the module is disabled.

## Examples

    # When enabled (any number of languages)
    iex> PhoenixKit.Modules.Languages.get_display_languages()
    [%{"code" => "en-US", "name" => "English (United States)", ...}]

    # When disabled
    iex> PhoenixKit.Modules.Languages.get_display_languages()
    [%{"code" => "en-US", ...}, %{"code" => "es-ES", ...}, ...]  # Top 12 default

# `get_enabled_language_codes`

Gets a list of enabled language codes, sorted by position.

Returns a list of enabled language code strings.

## Examples

    iex> PhoenixKit.Modules.Languages.get_enabled_language_codes()
    ["en-US", "es-ES"]

# `get_enabled_languages`

Gets only enabled languages, sorted by position.

Returns a list of enabled language maps.

## Examples

    iex> PhoenixKit.Modules.Languages.get_enabled_languages()
    [%{"code" => "en-US", "name" => "English (United States)", ...}]

# `get_enabled_languages_by_continent`

Gets enabled languages grouped by continent.

Filters `get_languages_grouped_by_continent/0` to only include languages
that are currently enabled. Languages may appear under multiple continents
(same as the admin settings page). Returns `[{continent, [language_maps]}]`.

## Examples

    iex> PhoenixKit.Modules.Languages.get_enabled_languages_by_continent()
    [{"Asia", [%{code: "ja", name: "Japanese", ...}]}, {"Europe", [%{code: "de-DE", ...}]}, ...]

# `get_language`

Gets a specific language by its code.

Returns the language map if found, or nil if not found.

## Examples

    iex> PhoenixKit.Modules.Languages.get_language("es-ES")
    %{"code" => "es-ES", "name" => "Spanish (Spain)", "is_enabled" => true}

    iex> PhoenixKit.Modules.Languages.get_language("invalid")
    nil

# `get_language_codes`

Gets a list of all language codes.

Returns a list of language code strings.

## Examples

    iex> PhoenixKit.Modules.Languages.get_language_codes()
    ["en-US", "es-ES", "fr-FR"]

# `get_languages`

Gets all configured languages from the JSON setting.

Returns a list of language maps, or empty list if not configured.

## Examples

    iex> PhoenixKit.Modules.Languages.get_languages()
    [%{"code" => "en-US", "name" => "English (United States)", "is_default" => true, ...}]

    # When system is disabled:
    iex> PhoenixKit.Modules.Languages.get_languages()
    []

# `get_languages_grouped_by_continent`

Gets all available languages grouped by continent, then by country.

Returns a list of {continent, countries} tuples where countries is a list of
{country, flag, languages} tuples. Sorted alphabetically by continent and country.
Only shows languages that are actually used in each country based on the
`language_locales` field from BeamLabCountries.

## Examples

    iex> PhoenixKit.Modules.Languages.get_languages_grouped_by_continent()
    [
      {"Africa", [{"South Africa", "🇿🇦", [%{code: "en-ZA", ...}]}, ...]},
      {"Asia", [{"China", "🇨🇳", [%{code: "zh-CN", ...}]}, ...]},
      {"Europe", [{"Germany", "🇩🇪", [%{code: "de-DE", ...}]}, ...]},
      ...
    ]

# `get_predefined_language`

Gets a predefined language by code.

Returns the language definition from the available languages list.

## Examples

    iex> PhoenixKit.Modules.Languages.get_predefined_language("es-ES")
    %{code: "es-ES", name: "Spanish (Spain)", native: "Español (España)", flag: "🇪🇸"}

    iex> PhoenixKit.Modules.Languages.get_predefined_language("invalid")
    nil

# `in_configured_mode?`

Checks if currently in configured mode (showing user-configured languages vs defaults).

Returns true if the module is enabled and has 2+ configured languages.
Returns false if showing the default top 10 languages.

## Examples

    iex> PhoenixKit.Modules.Languages.in_configured_mode?()
    true  # When enabled with 2+ languages

    iex> PhoenixKit.Modules.Languages.in_configured_mode?()
    false  # When disabled or only 1 language

# `language_enabled?`

Checks if a language is enabled.

Returns true if the language exists and is enabled, false otherwise.

## Examples

    iex> PhoenixKit.Modules.Languages.language_enabled?("es-ES")
    true

    iex> PhoenixKit.Modules.Languages.language_enabled?("disabled_lang")
    false

# `move_language_down`

Moves a language down one position in the array.

Moves the language one index later in the languages array. Cannot move the last language down.

## Examples

    iex> PhoenixKit.Modules.Languages.move_language_down("en-US")
    {:ok, updated_config}

    iex> PhoenixKit.Modules.Languages.move_language_down("es-ES")  # if last in array
    {:error, "Language is already at the bottom"}

# `move_language_up`

Moves a language up one position in the array.

Moves the language one index earlier in the languages array. Cannot move the first language up.

## Examples

    iex> PhoenixKit.Modules.Languages.move_language_up("es-ES")
    {:ok, updated_config}

    iex> PhoenixKit.Modules.Languages.move_language_up("en-US")  # if first in array
    {:error, "Language is already at the top"}

# `normalize_language_settings`

Normalizes language settings by merging any languages from the legacy
`admin_languages` setting into the unified `languages_config`.

This is a one-time migration function that:
1. Reads the old `admin_languages` JSON array of codes
2. Ensures the Languages module is enabled if admin languages existed
3. Adds any admin-only languages to the unified config
4. Clears the old `admin_languages` setting to prevent re-processing

Idempotent — if `admin_languages` doesn't exist or is empty, this is a no-op.

## Examples

    iex> PhoenixKit.Modules.Languages.normalize_language_settings()
    :ok

# `remove_language`

Removes a language from the system.

Cannot remove the default language or the last remaining language.

## Examples

    iex> PhoenixKit.Modules.Languages.remove_language("es-ES")
    {:ok, updated_config}

    iex> PhoenixKit.Modules.Languages.remove_language("en-US")  # if it's default
    {:error, "Cannot remove default language"}

# `set_default_language`

Sets a new default language.

Removes default status from all other languages and sets the specified language as default.

## Examples

    iex> PhoenixKit.Modules.Languages.set_default_language("es-ES")
    {:ok, updated_config}

    iex> PhoenixKit.Modules.Languages.set_default_language("nonexistent")
    {:error, "Language not found"}

# `update_language`

Updates an existing language in the system.

Takes a language code and map of attributes to update.

## Examples

    iex> PhoenixKit.Modules.Languages.update_language("es-ES", %{name: "Español"})
    {:ok, updated_config}

    iex> PhoenixKit.Modules.Languages.update_language("nonexistent", %{name: "Test"})
    {:error, "Language not found"}

# `valid_language?`

Checks if a language code is valid (exists in configuration).

Returns true if the language exists, false otherwise.

## Examples

    iex> PhoenixKit.Modules.Languages.valid_language?("es-ES")
    true

    iex> PhoenixKit.Modules.Languages.valid_language?("invalid")
    false

---

*Consult [api-reference.md](api-reference.md) for complete listing*
