1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 sanitizeUTF8Capitalization =
9 lang: (lib.replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] lang);
10 aggregatedLocales = [
11 "${config.i18n.defaultLocale}/${config.i18n.defaultCharset}"
12 ]
13 ++ lib.pipe config.i18n.extraLocaleSettings [
14 # See description of extraLocaleSettings for why is this ignored here.
15 (lib.filterAttrs (n: v: n != "LANGUAGE"))
16 (lib.mapAttrs (n: v: (sanitizeUTF8Capitalization v)))
17 (lib.mapAttrsToList (LCRole: lang: lang + "/" + (config.i18n.localeCharsets.${LCRole} or "UTF-8")))
18 ]
19 ++ (builtins.map sanitizeUTF8Capitalization (
20 lib.optionals (builtins.isList config.i18n.extraLocales) config.i18n.extraLocales
21 ))
22 ++ (lib.optional (builtins.isString config.i18n.extraLocales) config.i18n.extraLocales);
23in
24{
25 ###### interface
26
27 options = {
28
29 i18n = {
30 glibcLocales = lib.mkOption {
31 type = lib.types.path;
32 default = pkgs.glibcLocales.override {
33 allLocales = lib.any (x: x == "all") config.i18n.supportedLocales;
34 locales = config.i18n.supportedLocales;
35 };
36 defaultText = lib.literalExpression ''
37 pkgs.glibcLocales.override {
38 allLocales = lib.any (x: x == "all") config.i18n.supportedLocales;
39 locales = config.i18n.supportedLocales;
40 }
41 '';
42 example = lib.literalExpression "pkgs.glibcLocales";
43 description = ''
44 Customized pkg.glibcLocales package.
45
46 Changing this option can disable handling of i18n.defaultLocale
47 and supportedLocale.
48 '';
49 };
50
51 defaultLocale = lib.mkOption {
52 type = lib.types.str;
53 default = "en_US.UTF-8";
54 example = "nl_NL.UTF-8";
55 description = ''
56 The default locale. It determines the language for program messages,
57 the format for dates and times, sort order, and so on. Setting the
58 default character set is done via {option}`i18n.defaultCharset`.
59 '';
60 };
61 defaultCharset = lib.mkOption {
62 type = lib.types.str;
63 default = "UTF-8";
64 example = "ISO-8859-8";
65 description = ''
66 The default locale character set.
67 '';
68 };
69
70 extraLocales = lib.mkOption {
71 type = lib.types.either (lib.types.listOf lib.types.str) (lib.types.enum [ "all" ]);
72 default = [ ];
73 example = [ "nl_NL.UTF-8/UTF-8" ];
74 description = ''
75 Additional locales that the system should support, besides the ones
76 configured with {option}`i18n.defaultLocale` and
77 {option}`i18n.extraLocaleSettings`.
78 Set this to `"all"` to install all available locales.
79 '';
80 };
81
82 extraLocaleSettings = lib.mkOption {
83 type = lib.types.attrsOf lib.types.str;
84 default = { };
85 example = {
86 LC_MESSAGES = "en_US.UTF-8";
87 LC_TIME = "de_DE.UTF-8";
88 };
89 description = ''
90 A set of additional system-wide locale settings other than `LANG`
91 which can be configured with {option}`i18n.defaultLocale`. Note that
92 the `/UTF-8` suffix used in {option}`i18n.extraLocales` indicates a
93 character set, and it must not be added manually here. To use a
94 non-`UTF-8` character set such as ISO-XXXX-8, the
95 {option}`i18n.localeCharsets` can be used.
96
97 Note that if the [`LANGUAGE`
98 key](https://www.gnu.org/software/gettext/manual/html_node/The-LANGUAGE-variable.html)
99 is used in this option, it is ignored when computing the locales
100 required to be installed, because the possible values of this key are
101 more diverse and flexible then the others.
102 '';
103 };
104 localeCharsets = lib.mkOption {
105 type = lib.types.attrsOf lib.types.str;
106 default = { };
107 example = {
108 LC_MESSAGES = "ISO-8859-15";
109 LC_TIME = "ISO-8859-1";
110 };
111 description = ''
112 Per each {option}`i18n.extraLocaleSettings`, choose the character set
113 to use for it. Essentially defaults to UTF-8 for all of them.
114 '';
115 };
116
117 supportedLocales = lib.mkOption {
118 type = lib.types.listOf lib.types.str;
119 visible = false;
120 default = lib.unique (
121 [
122 "C.UTF-8/UTF-8"
123 "en_US.UTF-8/UTF-8"
124 ]
125 ++ aggregatedLocales
126 );
127 example = [
128 "en_US.UTF-8/UTF-8"
129 "nl_NL.UTF-8/UTF-8"
130 "nl_NL/ISO-8859-1"
131 ];
132 description = ''
133 List of locales that the system should support. The value
134 `"all"` means that all locales supported by
135 Glibc will be installed. A full list of supported locales
136 can be found at <https://sourceware.org/git/?p=glibc.git;a=blob;f=localedata/SUPPORTED>.
137 '';
138 };
139
140 };
141
142 };
143
144 ###### implementation
145
146 config = {
147 warnings =
148 lib.optional
149 (
150 !(
151 (lib.subtractLists config.i18n.supportedLocales aggregatedLocales) == [ ]
152 || lib.any (x: x == "all") config.i18n.supportedLocales
153 )
154 )
155 ''
156 `i18n.supportedLocales` is deprecated in favor of `i18n.extraLocales`,
157 and it seems you are using `i18n.supportedLocales` and forgot to
158 include some locales specified in `i18n.defaultLocale`,
159 `i18n.extraLocales` or `i18n.extraLocaleSettings`.
160
161 If you're trying to install additional locales not specified in
162 `i18n.defaultLocale` or `i18n.extraLocaleSettings`, consider adding
163 only those locales to `i18n.extraLocales`.
164 '';
165
166 environment.systemPackages =
167 # We increase the priority a little, so that plain glibc in systemPackages can't win.
168 lib.optional (config.i18n.supportedLocales != [ ]) (lib.setPrio (-1) config.i18n.glibcLocales);
169
170 environment.sessionVariables = {
171 LANG = config.i18n.defaultLocale;
172 LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
173 }
174 // config.i18n.extraLocaleSettings;
175
176 systemd.globalEnvironment = lib.mkIf (config.i18n.supportedLocales != [ ]) {
177 LOCALE_ARCHIVE = "${config.i18n.glibcLocales}/lib/locale/locale-archive";
178 };
179
180 # ‘/etc/locale.conf’ is used by systemd.
181 environment.etc."locale.conf".source = pkgs.writeText "locale.conf" ''
182 LANG=${config.i18n.defaultLocale}
183 ${lib.concatStringsSep "\n" (
184 lib.mapAttrsToList (n: v: "${n}=${v}") config.i18n.extraLocaleSettings
185 )}
186 '';
187
188 };
189}