Skip to content

lightweight C++17 header-only localization library powered by JSON. Fast, thread-safe, reloadable, and developer-friendly.

License

Notifications You must be signed in to change notification settings

0x1mer/Localizer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🗣️ Localizer

Header-only, JSON-driven localization library for modern C++

A lightweight and expressive C++17 header-only library for handling
JSON-based localization files — thread-safe, reloadable at runtime,
and fully customizable through user-defined callbacks.


📑 Table of Contents


✨ Key Features

  • Header-only — simply include Localizer.h, no build step required
  • JSON-based translations — powered by nlohmann::json
  • Thread-safe design using std::shared_mutex for concurrent access
  • Live reload — automatically detects and reloads modified JSON files
  • Customizable error handling via user-defined callbacks or ANSI-colored logger
  • Lightweight — no external dependencies beyond <nlohmann/json.hpp>
  • Debug mode — shows translation keys and colorized debug output for development

🧩 Installation

  1. Add the headers to your project:

    /include/Localizer.h
    /include/json.hpp              // nlohmann::json
    
  2. Include it in your code:

    #include <Localizer.h>
  3. (Optional) Add include paths in CMake or your IDE:

    target_include_directories(MyApp PRIVATE include)
    target_compile_features(MyApp PRIVATE cxx_std_17)

⚡ Quick Start

#include <iostream>
#include "Localizer.h"

int main() {
    // Load all JSON localization files from a folder
    Localizer::loadFromDirectory("langs");

    // Set the active locale
    Localizer::setLocale("en");

    // Simple translation
    std::cout << L("ui.button.play") << std::endl;

    // Placeholder example
    LocalizedString greet("ui.greeting", {{"user", "Oksi"}});
    std::cout << greet << std::endl;
}

Output:

Play
Hello, Oksi!

📄 Example JSON Configuration

{
  "en": {
    "ui": {
      "button": {
        "play": "Play",
        "exit": "Exit"
      },
      "greeting": "Hello, {user}!"
    }
  },
  "ru": {
    "ui": {
      "button": {
        "play": "Играть",
        "exit": "Выход"
      },
      "greeting": "Привет, {user}!"
    }
  }
}

Directory structure:

langs/
 ├─ ui.json
 ├─ errors.json
 └─ gameplay.json

Each file is treated as a namespace.
For example, ui.json → keys like ui.button.play, ui.greeting.


🛠️ Configuration via define Macros

Macro Default Description
LOC_THREAD_SAFE 1 Enables thread-safety using shared_mutex
LOC_USE_REGEX 0 Enables regex placeholder parsing
LOC_CERR 0 1 — log errors to std::cerr, 0 — use callback
LOC_DEFAULT_LOCALE "en" Fallback locale
LOC_NAMESPACE_SEPARATOR "." Separator for nested JSON keys
LOC_COLOR_DEFAULT "\x1b[32m" ANSI color for debug
LOC_COLOR_RESET "\x1b[0m" ANSI reset color code

Example:

#define LOC_CERR 0
#define LOC_THREAD_SAFE 1
#define LOC_USE_REGEX 0

#include "Localizer.h"

🪶 Custom Error Callback Example

By default, Localizer prints errors to std::cerr in red.
If you want full control — attach your custom error callback.

⚠️ Important:
The callback must be set before calling Localizer::loadFromDirectory() or any other function that may produce errors.

#define LOC_CERR 0
#include "Localizer.h"

void onError(const std::string& msg, int code) {
    std::cout << "\x1b[31m[Error " << code << "] " << msg << "\x1b[0m\n";
}

int main() {
    // Attach callback BEFORE loading any JSON files
    Localizer::setErrorCallback(onError);

    // Then safely load translations
    Localizer::loadFromDirectory("langs");
}

Output:

[Error 0] Cannot open language file: langs/missing.json

🌍 Changing Locale at Runtime

You can freely switch between languages during runtime —
but keep in mind how stored variables behave.

⚠️ Important:
If you save a localized string (e.g., LocalizedString) while one locale is active,
and then change the locale — that saved object will still display the old translation.
To get the updated language, you must recreate or retranslate it.

// Set locale to Russian
Localizer::setLocale("ru");
LocalizedString exitBtn("ui.button.exit");
std::cout << exitBtn << std::endl; // -> "Выход"

// Change to English later
Localizer::setLocale("en");

// Still shows the OLD translation because the string was stored earlier
std::cout << exitBtn << std::endl; // -> "Выход"

// To get updated text, recreate it:
LocalizedString updated("ui.button.exit");
std::cout << updated << std::endl; // -> "Exit"

🧠 Explanation:
LocalizedString stores the resolved value at creation time.
Changing the locale after that does not retroactively update previously created instances.


🎨 Debug Mode

Debug mode helps visualize which keys are being accessed and what translations are returned.
It’s especially useful during development or when testing new localization files.

// Example parameters for placeholders
std::unordered_map<std::string, std::string> params = {
    {"username", "Oksi"},
    {"score", "9000"}
};

// Enable debug mode
DebugOptions opts;
opts.enabled = true;
opts.coloredOutput = true;
opts.keyColor = "\x1b[36m"; // cyan
opts.prefix = "[DBG] ";
Localizer::setDebugOptions(opts);

std::cout << "\n=== Debug mode ===\n";
std::cout << L("ui.button.play") << "\n";
std::cout << L("ui.menu.exit") << "\n";
std::cout << L("messages.welcome", params) << "\n";

Output:
Debug output

💡 Note:
You can toggle debug mode at runtime using:

Localizer::setDebugMode(true);  // Enable debug output
Localizer::setDebugMode(false); // Disable debug output

This is useful when you want to enable detailed localization logs only in specific parts of your program.

🧠 Explanation:
When DebugOptions.enabled is true, each translation is prefixed with its key,
optionally colorized and formatted according to the provided options.


📦 Stats Example

You can print a short summary of all loaded languages and their key counts.
Useful for debugging or verifying that all localization files were successfully loaded.

Localizer::printStats();

Output:

📦 Localizer loaded 2 languages:
  🌐 en -> 12 keys
  🌐 ru -> 12 keys

🧠 Explanation:
printStats() shows how many language packs are currently active in memory and how many keys each one contains.
This is a quick way to confirm whether your localization directory structure is parsed correctly.


📜 License

MIT © 0x1mer
Special thanks to 1args for motivation and inspiration.

About

lightweight C++17 header-only localization library powered by JSON. Fast, thread-safe, reloadable, and developer-friendly.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages