1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: (c) 2023 Hubert Figuière

//! Implement the proc macros.
//!
#![doc = include_str!("../README.md")]

use proc_macro::{Delimiter, Group, TokenStream, TokenTree};
use quote::quote;

#[proc_macro]
/// Wrapper macro to use [`gettextrs::gettext!`] or
/// [`gettextrs::ngettext!`] in a way that allow xgettext to find
/// strings for `.po` files as it doesn't support a keyword with a
/// `!`.
///
/// ```
/// use i18n_format::i18n_fmt;
///
/// let number = 1;
/// let s = i18n_fmt! {
///     i18n_fmt("This is number {}, make it so !", number)
/// };
///
/// let count = 2;
/// let message = i18n_fmt! {
///     i18n_nfmt("Counted {} item", "Counted {} items", count, count)
/// };
/// ```
///
/// Both `i18n_fmt` and `i18n_nfmt` are placeholders, in the block for
/// `i18n_fmt!` either will be replaced by a call to
/// [`gettextrs::gettext!`] or [`gettextrs::ngettext!`], respectively.
/// Specify `i18n_fmt` and `i18n_nfmt:1,2` as keywords for calls to
/// xgettext.
pub fn i18n_fmt(body: TokenStream) -> TokenStream {
    let mut macro_block: TokenStream = quote!(
        use gettextrs::*;
    )
    .into();
    macro_block.extend(body.into_iter().map(move |tt| {
        if let TokenTree::Ident(ref i) = tt {
            match i.to_string().as_str() {
                "i18n_fmt" => {
                    return TokenTree::Group(Group::new(Delimiter::None, quote!(gettext!).into()))
                }
                "i18n_nfmt" => {
                    return TokenTree::Group(Group::new(Delimiter::None, quote!(ngettext!).into()))
                }
                _ => {}
            }
        }
        tt
    }));

    [TokenTree::Group(Group::new(Delimiter::Brace, macro_block))]
        .into_iter()
        .collect()
}