clinuxprintfnon-ascii-characterswchar

Why does wprintf transliterate Russian text in Unicode into Latin on Linux?


Why does the following program

#include <stdio.h>
#include <wchar.h>

int main() {
  wprintf(L"Привет, мир!");
}

print "Privet, mir!" on Linux? Specifically, why does it transliterate Russian text in Unicode into Latin as opposed to transcoding it into UTF-8 or using replacement characters?

Demonstration of this behavior on Godbolt: https://godbolt.org/z/36zEcG

The non-wide version printf("Привет, мир!") prints this text as expected ("Привет, мир!").


Solution

  • Because conversion of wide characters is done according to the currently set locale. By default a C program always starts with a "C" locale which only supports ASCII characters.

    You have to switch to any Russian or UTF-8 locale first:

    setlocale(LC_ALL, "ru_RU.utf8"); // Russian Unicode
    setlocale(LC_ALL, "en_US.utf8"); // English US Unicode
    

    Or to a current system locale (which is likely what you need):

    setlocale(LC_ALL, "");
    

    The full program will be:

    #include <stdio.h>
    #include <wchar.h>
    #include <locale.h>
    
    int main() {
      setlocale(LC_ALL, "ru_RU.utf8");
      wprintf(L"Привет, мир!\n");
    }
    

    As for your code working as-is on other machines - this is due to how libc operates there. Some implementations (like musl) do not support non-Unicode locales and thus can unconditionally translate wide characters to an UTF-8 sequence.