1//! Macros that translate the message and then replace placeholders in it.
2
3/// This is an implementation detail for counting arguments in the gettext macros. Don't call this directly.
4#[macro_export]
5#[doc(hidden)]
6macro_rules! count_args {
7 () => { 0 };
8 ($_e: expr $(, $rest: expr)*) => { 1 + $crate::count_args!($($rest),*) }
9}
10
11/// This is an implementation detail for replacing arguments in the gettext macros. Don't call this directly.
12#[macro_export]
13#[doc(hidden)]
14macro_rules! freplace {
15 ($format:expr, $($args:expr),+ $(,)?) => {{
16 let mut parts = $format.split("{}");
17 debug_assert_eq!(parts.clone().count() - 1, $crate::count_args!($($args),*), "Argument count has to match number of format directives ({{}})");
18
19 let mut output = parts.next().unwrap_or_default().to_string();
20 $(
21 output += &format!("{}{}", $args, &parts.next().expect("Argument count has to match number of format directives ({})"));
22 )*
23 output
24 }};
25}
26
27/// Like [`gettext`], but allows for formatting.
28///
29/// It calls [`gettext`] on `msgid`, and then replaces each occurrence of `{}` with the next value
30/// out of `args`.
31///
32/// [`gettext`]: fn.gettext.html
33#[macro_export]
34macro_rules! gettext {
35 ($msgid:expr, $($args:expr),+ $(,)?) => {{
36 let format = $crate::gettext($msgid);
37 $crate::freplace!(format, $($args),*)
38 }};
39}
40
41/// Like [`dgettext`], but allows for formatting.
42///
43/// It calls [`dgettext`] on `domainname` and `msgid`, and then replaces each occurrence of `{}`
44/// with the next value out of `args`.
45///
46/// [`dgettext`]: fn.dgettext.html
47#[macro_export]
48macro_rules! dgettext {
49 ($domainname:expr, $msgid:expr, $($args:expr),+ $(,)?) => {{
50 let format = $crate::dgettext($domainname, $msgid);
51 $crate::freplace!(format, $($args),*)
52 }};
53}
54
55/// Like [`dcgettext`], but allows for formatting.
56///
57/// It calls [`dcgettext`] on `domainname`, `category`, and `msgid`, and then replaces each
58/// occurrence of `{}` with the next value out of `args`.
59///
60/// [`dcgettext`]: fn.dcgettext.html
61#[macro_export]
62macro_rules! dcgettext {
63 ($domainname:expr, $category:expr, $msgid:expr, $($args:expr),+ $(,)?) => {{
64 let format = $crate::dcgettext($domainname, $msgid, $category);
65 $crate::freplace!(format, $($args),*)
66 }};
67}
68
69/// Like [`ngettext`], but allows for formatting.
70///
71/// It calls [`ngettext`] on `msgid`, `msgid_plural`, and `n`, and then replaces each occurrence of
72/// `{}` with the next value out of `args`.
73///
74/// [`ngettext`]: fn.ngettext.html
75#[macro_export]
76macro_rules! ngettext {
77 ($msgid:expr, $msgid_plural:expr, $n:expr, $($args:expr),+ $(,)?) => {{
78 let format = $crate::ngettext($msgid, $msgid_plural, $n);
79 $crate::freplace!(format, $($args),*)
80 }}
81}
82
83/// Like [`dngettext`], but allows for formatting.
84///
85/// It calls [`dngettext`] on `domainname`, `msgid`, `msgid_plural`, and `n`, and then replaces
86/// each occurrence of `{}` with the next value out of `args`.
87///
88/// [`dngettext`]: fn.dngettext.html
89#[macro_export]
90macro_rules! dngettext {
91 ($domainname:expr, $msgid:expr, $msgid_plural:expr, $n:expr, $($args:expr),+ $(,)?) => {{
92 let format = $crate::dngettext($domainname, $msgid, $msgid_plural, $n);
93 $crate::freplace!(format, $($args),*)
94 }}
95}
96
97/// Like [`dcngettext`], but allows for formatting.
98///
99/// It calls [`dcngettext`] on `domainname`, `category`, `msgid`, `msgid_plural`, and `n`, and then
100/// replaces each occurrence of `{}` with the next value out of `args`.
101///
102/// [`dcngettext`]: fn.dcngettext.html
103#[macro_export]
104macro_rules! dcngettext {
105 ($domainname:expr, $category:expr, $msgid:expr, $msgid_plural:expr, $n:expr, $($args:expr),+ $(,)?) => {{
106 let format = $crate::dcngettext($domainname, $msgid, $msgid_plural, $n, $category);
107 $crate::freplace!(format, $($args),*)
108 }}
109}
110
111/// Like [`pgettext`], but allows for formatting.
112///
113/// It calls [`pgettext`] on `msgctxt` and `msgid`, and then replaces each occurrence of `{}` with
114/// the next value out of `args`.
115///
116/// [`pgettext`]: fn.pgettext.html
117#[macro_export]
118macro_rules! pgettext {
119 ($msgctxt:expr, $msgid:expr, $($args:expr),+ $(,)?) => {{
120 let format = $crate::pgettext($msgctxt, $msgid);
121 $crate::freplace!(format, $($args),*)
122 }}
123}
124
125/// Like [`npgettext`], but allows for formatting.
126///
127/// It calls [`npgettext`] on `msgctxt`, `msgid`, `msgid_plural`, and `n`, and then replaces each
128/// occurrence of `{}` with the next value out of `args`.
129///
130/// [`npgettext`]: fn.npgettext.html
131#[macro_export]
132macro_rules! npgettext {
133 ($msgctxt:expr, $msgid:expr, $msgid_plural:expr, $n:expr, $($args:expr),+ $(,)?) => {{
134 let format = $crate::npgettext($msgctxt, $msgid, $msgid_plural, $n);
135 $crate::freplace!(format, $($args),*)
136 }}
137}
138
139#[cfg(test)]
140mod test {
141 use crate::*;
142
143 #[test]
144 fn test_gettext_macro() {
145 setlocale(LocaleCategory::LcAll, "en_US.UTF-8");
146
147 bindtextdomain("hellorust", "/usr/local/share/locale").unwrap();
148 textdomain("hellorust").unwrap();
149
150 assert_eq!(gettext!("Hello, {}!", "world"), "Hello, world!");
151 assert_eq!(
152 gettext!("Hello, {} {}!", "small", "world"),
153 "Hello, small world!"
154 );
155 }
156
157 #[test]
158 fn test_ngettext_macro() {
159 setlocale(LocaleCategory::LcAll, "en_US.UTF-8");
160
161 bindtextdomain("hellorust", "/usr/local/share/locale").unwrap();
162 textdomain("hellorust").unwrap();
163
164 assert_eq!(
165 ngettext!("Singular {}!", "Multiple {}!", 2, "Worlds"),
166 "Multiple Worlds!"
167 );
168 }
169
170 #[test]
171 fn test_pgettext_macro() {
172 setlocale(LocaleCategory::LcAll, "en_US.UTF-8");
173
174 bindtextdomain("hellorust", "/usr/local/share/locale").unwrap();
175 textdomain("hellorust").unwrap();
176
177 assert_eq!("Hello, world!", pgettext!("context", "Hello, {}!", "world"));
178 }
179
180 #[test]
181 fn test_npgettext_macro() {
182 setlocale(LocaleCategory::LcAll, "en_US.UTF-8");
183
184 bindtextdomain("hellorust", "/usr/local/share/locale").unwrap();
185 textdomain("hellorust").unwrap();
186
187 assert_eq!(
188 "Multiple Worlds!",
189 npgettext!("context", "Singular {}!", "Multiple {}!", 2, "Worlds")
190 );
191 }
192
193 #[test]
194 fn test_dgettext_macro() {
195 setlocale(LocaleCategory::LcAll, "en_US.UTF-8");
196
197 bindtextdomain("hellorust", "/usr/local/share/locale").unwrap();
198
199 assert_eq!(
200 "Hello, world!",
201 dgettext!("hellorust", "Hello, {}!", "world")
202 );
203 }
204
205 #[test]
206 fn test_dcgettext_macro() {
207 setlocale(LocaleCategory::LcAll, "en_US.UTF-8");
208
209 bindtextdomain("hellorust", "/usr/local/share/locale").unwrap();
210
211 assert_eq!(
212 "Hello, world!",
213 dcgettext!("hellorust", LocaleCategory::LcAll, "Hello, {}!", "world")
214 );
215 }
216
217 #[test]
218 fn test_dcngettext_macro() {
219 setlocale(LocaleCategory::LcAll, "en_US.UTF-8");
220
221 bindtextdomain("hellorust", "/usr/local/share/locale").unwrap();
222
223 assert_eq!(
224 "Singular World",
225 dcngettext!(
226 "hellorust",
227 LocaleCategory::LcAll,
228 "Singular {}",
229 "Multiple {}",
230 1,
231 "World"
232 )
233 )
234 }
235
236 #[test]
237 fn test_dngettext_macro() {
238 setlocale(LocaleCategory::LcAll, "en_US.UTF-8");
239
240 bindtextdomain("hellorust", "/usr/local/share/locale").unwrap();
241
242 assert_eq!(
243 "Singular World!",
244 dngettext!("hellrust", "Singular {}!", "Multiple {}!", 1, "World")
245 )
246 }
247}
248