Skip to content
Luni edited this page Dec 7, 2025 · 3 revisions

Welcome to the WFLU (Wyvern Fluent Language) documentation! WFLU is a lightweight i18n library for projects.

Quick Start

using Wyvern.Localization.WFLU;

var localization = new Localization();

// Load multiple files for the same language - they merge together
localization.LoadFile("en-US", "locales/en-US/core.wflu");
localization.LoadFile("en-US", "locales/en-US/ui.wflu");
localization.LoadFile("es-ES", "locales/es-ES/core.wflu");

// Set the active language
localization.CurrentLanguage = "en-US";

// Get messages
string greeting = localization.Get("greeting");
string apples = localization.Get("apples", new Dictionary<string, object> { { "count", 5 } });

File Format (.wflu)

Simple Messages

greeting = Hello
farewell = Goodbye
welcome_message = Welcome, {username}!

Choice Messages (Pluralization)

apples = ->
    [one] You have 1 apple
    *[other] You have {count} apples
}

items = ->
    [one] {count} item
    *[other] {count} items
}

Comments and Empty Lines

# This is a comment
greeting = Hello

# Comments and empty lines are ignored
farewell = Goodbye

Localization Class

The main class for managing and retrieving localized messages.

Properties

CurrentLanguage

public string CurrentLanguage { get; set; }

Gets or sets the currently active language. Throws ArgumentException if the language hasn't been loaded yet.

Example:

localization.CurrentLanguage = "en-US";

LoadedLanguages

public IEnumerable<string> LoadedLanguages { get; }

Returns all languages that have been loaded.

Example:

foreach (var lang in localization.LoadedLanguages)
{
    Console.WriteLine(lang); // en-US, es-ES, fr-FR, etc.
}

Methods

LoadFile(string language, string path)

public void LoadFile(string language, string path)

Loads a .wflu file for the specified language. You can call this multiple times with the same language to load multiple files - all messages will merge together. If a key exists in multiple files, the last loaded file wins.

Parameters:

  • language - Language code (e.g., "en-US", "es-ES")
  • path - Path to the .wflu file

Example:

// Load multiple files for the same language
localization.LoadFile("en-US", "locales/en-US/core.wflu");
localization.LoadFile("en-US", "locales/en-US/ui.wflu");
localization.LoadFile("en-US", "locales/en-US/errors.wflu");

// Load files for different languages
localization.LoadFile("es-ES", "locales/es-ES/core.wflu");
localization.LoadFile("fr-FR", "locales/fr-FR/core.wflu");

Get(string key, Dictionary<string, object>? variables = null)

public string Get(string key, Dictionary<string, object>? variables = null)

Retrieves a message by key from the current language. Returns !key! if the key or language is not found.

Parameters:

  • key - Message key to retrieve
  • variables - Optional dictionary of variables for substitution and choice selection

Example:

// Simple message
string greeting = localization.Get("greeting");
// Output: "Hello"

// Message with variables
string welcome = localization.Get("welcome_message", 
    new Dictionary<string, object> { { "username", "Alice" } });
// Output: "Welcome, Alice!"

// Choice message (pluralization)
string apples1 = localization.Get("apples", 
    new Dictionary<string, object> { { "count", 1 } });
// Output: "You have 1 apple"

string apples5 = localization.Get("apples", 
    new Dictionary<string, object> { { "count", 5 } });
// Output: "You have 5 apples"

// Missing key
string missing = localization.Get("nonexistent");
// Output: "!nonexistent!"

Keys(string? language = null)

public IEnumerable<string> Keys(string? language = null)

Returns all message keys for the specified language (or current language if not specified).

Example:

// Get keys for current language
foreach (var key in localization.Keys())
{
    Console.WriteLine(key);
}

// Get keys for specific language
foreach (var key in localization.Keys("es-ES"))
{
    Console.WriteLine(key);
}

IsLanguageLoaded(string language)

public bool IsLanguageLoaded(string language)

Check if a language has been loaded.

Example:

if (localization.IsLanguageLoaded("en-US"))
{
    localization.CurrentLanguage = "en-US";
}

Parser Class

Low-level class that parses .wflu files into Message objects. Most users won't need to use this directly - use Localization.LoadFile() instead.

Methods

ParseFile(string path)

public Dictionary<string, Message> ParseFile(string path)

Parses a .wflu file and returns a dictionary of messages.

Example:

var parser = new Parser();
var messages = parser.ParseFile("en.wflu");

// Access parsed messages
var greeting = messages["greeting"];
Console.WriteLine(greeting.RawValue); // "Hello"

var apples = messages["apples"];
if (apples.IsChoice)
{
    Console.WriteLine(apples.Choices["one"]);   // "You have 1 apple"
    Console.WriteLine(apples.Choices["other"]); // "You have {count} apples"
}

Message Class

Represents a single localized message.

Properties

  • RawValue (string) - The raw message text for simple messages
  • IsChoice (bool) - True if this is a choice message (pluralization)
  • Choices (Dictionary<string, string>) - Choice options for choice messages (e.g., "one", "other")

How Pluralization Works

When you call Get() with a count variable on a choice message:

  1. If count == 1 and a [one] choice exists, it uses that
  2. Otherwise, it uses the *[other] choice
  3. All {variables} in the selected choice are replaced

Example:

apples = ->
    [one] You have 1 apple
    *[other] You have {count} apples
}
localization.Get("apples", new Dictionary<string, object> { { "count", 1 } });
// Output: "You have 1 apple"

localization.Get("apples", new Dictionary<string, object> { { "count", 0 } });
// Output: "You have 0 apples"

localization.Get("apples", new Dictionary<string, object> { { "count", 5 } });
// Output: "You have 5 apples"

Best Practices

Organize Files by Feature

locales/
  en-US/
    core.wflu      # Common messages
    ui.wflu        # UI-specific messages
    errors.wflu    # Error messages
  es-ES/
    core.wflu
    ui.wflu
    errors.wflu

Load All Files at Startup

var localization = new Localization();

// Load all English files
localization.LoadFile("en-US", "locales/en-US/core.wflu");
localization.LoadFile("en-US", "locales/en-US/ui.wflu");
localization.LoadFile("en-US", "locales/en-US/errors.wflu");

// Load all Spanish files
localization.LoadFile("es-ES", "locales/es-ES/core.wflu");
localization.LoadFile("es-ES", "locales/es-ES/ui.wflu");
localization.LoadFile("es-ES", "locales/es-ES/errors.wflu");

// Set default language
localization.CurrentLanguage = "en-US";

Use Consistent Key Naming

# Good - namespace-style keys
ui.button.save = Save
ui.button.cancel = Cancel
error.network.timeout = Connection timed out

# Also good - snake_case keys
button_save = Save
button_cancel = Cancel
network_timeout_error = Connection timed out

Handle Missing Keys Gracefully

string message = localization.Get("some.key");
if (message.StartsWith("!") && message.EndsWith("!"))
{
    // Key not found, use fallback
    message = "Default message";
}

Common Patterns

Language Switcher

public void SwitchLanguage(string languageCode)
{
    if (localization.IsLanguageLoaded(languageCode))
    {
        localization.CurrentLanguage = languageCode;
        RefreshUI(); // Reload all displayed text
    }
    else
    {
        Console.WriteLine($"Language {languageCode} not available");
    }
}

List Available Languages

public void ShowAvailableLanguages()
{
    Console.WriteLine("Available languages:");
    foreach (var lang in localization.LoadedLanguages)
    {
        Console.WriteLine($"- {lang}");
    }
}

Dynamic Message Loading

// If you need to load additional messages at runtime
public void LoadFeatureMessages(string feature)
{
    foreach (var lang in localization.LoadedLanguages)
    {
        var path = $"locales/{lang}/{feature}.wflu";
        if (File.Exists(path))
        {
            localization.LoadFile(lang, path);
        }
    }
}