/++ Module singlang - Lightweight internationalization (i18n) support for D programs. Description: This module integrates GNU Gettext for translations in D applications, using a singleton `Singlang` class and a convenient `_()` function for message translation. Errors are reported to stderr for proper error handling. +/ module singlang; import core.stdc.locale : setlocale, LC_ALL; import std.string : toStringz; import std.conv : to; import std.stdio : writeln, stderr; extern(C) { char* bindtextdomain(const char* domainname, const char* dirname); char* textdomain(const char* domainname); char* gettext(const char* msgid); } /++ Class Singlang - Singleton manager for translation configuration. Description: The `Singlang` class encapsulates the setup and management of the GNU Gettext-based translation system. It ensures that the translation environment is initialized only once and provides a centralized configuration point for the domain and translation directory. Usage Example: --- import singlang; void main() { if (Singlang.set("myapp", "/usr/share/locale")) { writeln("Translation system ready!"); } } --- +/ class Singlang { private: /++ Translation domain name used by GNU Gettext +/ string domain; /++ Directory path containing translation files (.mo) +/ string localeDir; /++ Singleton instance of the Singlang class +/ static Singlang instance; /++ Private constructor for initializing a Singlang instance. Params: domain = The translation domain name localeDir = Path to the directory with translation files Note: Accessible only internally; use `set` to create an instance. +/ this(string domain, string localeDir) { this.domain = domain; this.localeDir = localeDir; } public: /++ Initializes the translation system with a domain and directory. Description: Configures the GNU Gettext environment by setting the translation directory and domain. This method must be called before using the `_()` function for translations. Errors are reported to stderr. Params: domain = Name of the translation domain (e.g., application name) localeDir = Path to the directory with translation files (.mo) Returns: `true` on successful initialization, `false` if an error occurs Notes: - Attempting to initialize an already initialized instance will fail with an error message to stderr. - Errors during directory or domain setup will reset the instance to null and log messages to stderr. Example: --- if (Singlang.set("myapp", "/path/to/translations")) { writeln("Ready to translate!"); } else { writeln("Setup failed, check stderr for details!"); } --- +/ static bool set(string domain, string localeDir) { if (instance !is null) { stderr.writeln("Error: Singlang is already initialized"); return false; } setlocale(LC_ALL, ""); instance = new Singlang(domain, localeDir); if (bindtextdomain(domain.toStringz, localeDir.toStringz) is null) { stderr.writeln("Error: Failed to set translations directory"); instance = null; return false; } if (textdomain(domain.toStringz) is null) { stderr.writeln("Error: Failed to set translations domain"); instance = null; return false; } return true; } } /++ Retrieves the translated version of a given message. Description: Translates a message using the configured `Singlang` instance. If the system is not initialized, it returns the original message and logs an error to stderr. Params: msg = The message to translate (UTF-8 encoded) Returns: The translated string, or the original message if translation fails or system is uninitialized Example: --- writeln(_("Error: File not found")); // Outputs translated text or original if unavailable --- +/ string _(string msg) { if (Singlang.instance is null) { stderr.writeln("Error: Singlang module is not initialized"); return msg; } auto result = gettext(msg.toStringz); return result !is null ? result.to!string : msg; }