Why should your C++ code speak all the world's languages?
\n\nImagine: you've written an ideal parser or a high-performance server in C++. It's blazing fast, uses minimal memory, but outputs messages only in Russian. And tomorrow, a Japanese corporation buys your product. What to do? Rewrite all the output? Insert hundreds of if (lang == "jp")? That's a path to hell.
Meet C++ internationalization (or simply i18n C++ — from the word "internationalization", where there are 18 letters between i and n). This is not just "translating buttons", but a whole design philosophy that allows your application to work correctly with any language, culture, and encoding. Without C++ internationalization, a project is doomed to a local niche. With it, you enter the global market.
\n\nIn this article, we'll break down how to tame C++ localization using standard tools and powerful libraries. No fluff — only working code and real-world cases.
\n\nThe three pillars of internationalization: Locale, Encoding, and Messages
\n\nBefore writing code, let's understand what C++ internationalization consists of in practice. You need to solve three tasks:
\n\n- \n
- Locale: How to format dates, numbers, and currency. In the US, they write 03/15/2024, in Germany — 15.03.2024. \n
- Encoding: How to work with Unicode. Cyrillic, Chinese characters, Arabic script — all of this must be stored and displayed without garbled text. \n
- Messages: How to store and substitute translated strings. "File not found" in Russian is "Файл не найден". \n
Ignoring even one point will turn your application into a pumpkin. Let's go in order.
\n\nBasic level: Standard locale and std::locale
\n\nThe C++ standard library provides the std::locale class. This is your first tool for C++ internationalization. It can change decimal separators and date output formats.
Example: formatting numbers and dates
\n\n#include <iostream>\n#include <locale>\n#include <ctime>\n#include <iomanip>\n\nvoid print_date(double value) {\n std::cout.imbue(std::locale("en_US.UTF-8"));\n std::cout << "EN: " << value << std::endl;\n\n std::cout.imbue(std::locale("de_DE.UTF-8"));\n std::cout << "DE: " << value << std::endl;\n\n std::cout.imbue(std::locale("ru_RU.UTF-8"));\n std::cout << "RU: " << value << std::endl;\n}\n\nint main() {\n double pi = 3.14159;\n print_date(pi);\n\n // Working with dates\n std::time_t t = std::time(nullptr);\n std::tm* tm = std::localtime(&t);\n\n std::cout.imbue(std::locale("en_US.UTF-8"));\n std::cout << "EN date: " << std::put_time(tm, "%x") << std::endl;\n\n std::cout.imbue(std::locale("ru_RU.UTF-8"));\n std::cout << "RU date: " << std::put_time(tm, "%x") << std::endl;\n\n return 0;\n}\n\nImportant: Locale names (e.g., "ru_RU.UTF-8") depend on the operating system. On Windows, they might be called "Russian_Russia.1251". This is the first pain point. Standard C++ localization via std::locale is fine for simple tasks, but for serious projects, it's better to use specialized libraries.
Advanced level: Boost.Locale — the internationalizer's best friend
\n\nIf you're writing in C++ and want to do C++ internationalization correctly — install Boost.Locale. This library solves all three problems: it can work with Unicode, format anything, and extract translated strings from .po and .mo files (GNU gettext format).
\n\nInstallation and setup
\nBoost.Locale is part of Boost. Include it in your CMakeLi