The scenario
You installed Fedora on a machine that shipped with a different regional configuration, or you switched to a minimal server spin and every terminal prompt, man page, and error message is in a language you do not read. You changed the setting in GNOME or KDE, but the terminal still outputs English dates and French error codes. You need the system to speak one consistent language, and you need it to stick across reboots, new SSH sessions, and background services.
What locale actually does
The word locale sounds like it only controls translation. It does much more. A locale is a collection of environment variables that tell applications how to format numbers, dates, currency, collation order, and character classification. It also tells the C library which character encoding to expect. When you set LANG=de_DE.UTF-8, you are not just asking for German menus. You are telling sort to order accented characters correctly, telling ls to display file sizes in kilobytes instead of kibibytes, and telling date to format timestamps according to German conventions.
Think of the locale as a shared contract between the operating system and every program that reads or writes text. If the contract says UTF-8, every application assumes every byte stream follows that standard. If the contract says ISO-8859-1, modern terminals will render replacement characters and scripts will fail to parse filenames. Fedora defaults to C.UTF-8 for the system base because it is fast, predictable, and works everywhere. Desktop environments and user sessions override that default with your preferred language.
The LC_* variables control individual categories. LC_CTYPE handles character classification and case conversion. LC_NUMERIC handles decimal points and thousands separators. LC_TIME handles date and time formatting. LC_MESSAGES controls application output translation. LANG acts as the fallback for any category that does not have an explicit LC_* override. Setting LANG alone is usually enough. The system inherits the encoding and language for all subcategories automatically.
Run locale -a to see what is available. The list comes from the glibc package. Fedora ships with a compressed locale archive to save disk space. You only generate the locales you actually use.
Reboot before you debug. Half the time the symptom is gone.
How to set it properly
Fedora uses systemd to manage system-wide environment variables. The cleanest way to change the locale is through localectl, which writes to the correct configuration files and updates the systemd environment manager in one step. You do not need to manually edit /etc/locale.conf unless you are troubleshooting a broken configuration. Config files in /etc/ are user-modified. Files in /usr/lib/ ship with the package. Edit /etc/. Never edit /usr/lib/.
First, check which locales are currently compiled on your system. The glibc package ships with a compressed list of available locales. You only need to generate the ones you actually use.
# List all available locales that are already generated
locale -a
If your target locale is missing, generate it. The locale-gen command reads the /etc/locale.gen file and compiles the requested locales into binary format. This step is fast and safe.
# Generate the German locale with UTF-8 encoding
sudo locale-gen de_DE.UTF-8
Now apply the change system-wide. The localectl command updates /etc/locale.conf, writes the variables to the systemd environment, and ensures the change survives a reboot.
# Set the system default language and character set
sudo localectl set-locale LANG=de_DE.UTF-8
If you are running a desktop session, you need to reload the environment for your current user. The localectl command only updates the system base. Your login manager or display server will pick up the new setting on the next login. You can force the current terminal to adopt it immediately.
# Reload the current shell environment with the new locale
export LANG=de_DE.UTF-8
For server environments without a display manager, you can apply the change to all active services by reloading the systemd daemon. This propagates the new environment variables to every running unit.
# Tell systemd to reload its environment manager
sudo systemctl daemon-reload
dnf upgrade --refresh is the normal weekly maintenance command. dnf system-upgrade is for crossing major Fedora releases. They are different commands. Don't conflate them.
Verify the change
Run a quick check to confirm the system is reading the correct variables. The locale command without arguments prints every LC_* variable and the fallback LANG value.
# Print the current locale configuration for the active session
locale
You should see LANG=de_DE.UTF-8 at the top. If you see LANG=C.UTF-8 or LANG=en_US.UTF-8, the change did not apply to your current session. Log out and back in, or run the export command again. Check the system-wide configuration file to ensure it was written correctly.
# Read the persistent system locale configuration
cat /etc/locale.conf
The file should contain a single line matching your target locale. If the file is empty or contains conflicting variables, systemd will fall back to the compiled default. You can also query the systemd environment manager directly to see what running services will inherit.
# Check the systemd environment for the LANG variable
systemctl show-environment | grep LANG
If the output is empty, the environment manager has not picked up the change yet. Run sudo systemctl daemon-reload again. Trust the package manager. Manual file edits drift, snapshots stay.
Common pitfalls and error messages
The most frequent mistake is mixing character encodings. Applications expect a consistent encoding across all LC_* variables. If you set LANG=de_DE.UTF-8 but leave LC_CTYPE=C, text rendering breaks and terminal emulators display replacement characters. Always set LANG and let the system inherit the encoding for all subcategories, or explicitly set every LC_* variable to the same value.
Another common issue is editing /etc/locale.conf manually without updating the systemd environment. The file is read at boot, but running services keep their old environment variables in memory. You will see the correct value in the file, but locale will still report the old one until you reboot or run systemctl daemon-reload.
If you encounter this error during package installation or system updates:
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
The system is trying to use a locale that was never generated. Run sudo locale-gen with the missing code, then reload the environment. Do not ignore the warning. Broken locale settings cause dnf to mangle package names and break dependency resolution.
The LC_ALL variable overrides everything. If you see LC_ALL=C in your environment, it will force every application to use the POSIX C locale regardless of LANG. This is useful for scripting and log parsing, but it breaks desktop translation. Remove LC_ALL from your shell profile if you want language support to work normally.
journalctl -xe reads better than journalctl alone. The x flag adds explanatory text and the e flag jumps to the end. Most sysadmins type journalctl -xeu <unit> muscle-memory style.
Choose the right tool for your setup
Use localectl set-locale when you want a clean, systemd-aware change that updates both the configuration file and the running environment. Use locale-gen when you need to compile a new language pack that is not currently available on the system. Use manual /etc/locale.conf edits only when you are recovering a broken configuration or working on a minimal container without systemd. Stick to LANG overrides in your ~/.bashrc or ~/.profile when you need a per-user language setting that does not affect system services.
Run journalctl first. Read the actual error before guessing.