summaryrefslogtreecommitdiff
path: root/zen/i18n.h
blob: 340473f939aee71820af3ff26210fb087dcf2afd (plain)
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// **************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under *
// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0        *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved        *
// **************************************************************************

#ifndef I18_N_H_3843489325044253425456
#define I18_N_H_3843489325044253425456

#include <string>
#include <memory>
#include <cstdint>
#include "string_tools.h"
#include "format_unit.h"

//minimal layer enabling text translation - without platform/library dependencies!
#ifdef __WXMSW__ //we have wxWidgets
    #ifndef WXINTL_NO_GETTEXT_MACRO
        #error WXINTL_NO_GETTEXT_MACRO must be defined to deactivate wxWidgets underscore macro
    #endif
#endif

#define ZEN_TRANS_CONCAT_SUB(X, Y) X ## Y
#define _(s) zen::implementation::translate(ZEN_TRANS_CONCAT_SUB(L, s))
#define _P(s, p, n) zen::implementation::translate(ZEN_TRANS_CONCAT_SUB(L, s), ZEN_TRANS_CONCAT_SUB(L, p), n)
//source and translation are required to use %x as number placeholder
//for plural form, which will be substituted automatically!!!

namespace zen
{
//implement handler to enable program-wide localizations:
struct TranslationHandler
{
    //THREAD-SAFETY: "const" member must model thread-safe access!
    TranslationHandler() {}
    virtual ~TranslationHandler() {}

    //C++11: std::wstring should be thread-safe like an int
    virtual std::wstring translate(const std::wstring& text) const = 0; //simple translation
    virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, std::int64_t n) const = 0;

private:
    TranslationHandler           (const TranslationHandler&) = delete;
    TranslationHandler& operator=(const TranslationHandler&) = delete;
};

void setTranslator(std::unique_ptr<const TranslationHandler>&& newHandler); //take ownership
const TranslationHandler* getTranslator();













//######################## implementation ##############################
namespace implementation
{
inline
std::wstring translate(const std::wstring& text)
{
    if (const TranslationHandler* t = getTranslator())
        return t->translate(text);

    return text;
}


//translate plural forms: "%x day" "%x days"
//returns "1 day" if n == 1; "123 days" if n == 123 for english language
inline
std::wstring translate(const std::wstring& singular, const std::wstring& plural, std::int64_t n)
{
    assert(contains(plural, L"%x"));

    if (const TranslationHandler* t = getTranslator())
    {
        std::wstring translation = t->translate(singular, plural, n);
        assert(!contains(translation, L"%x"));
        return translation;
    }

    return replaceCpy(std::abs(n) == 1 ? singular : plural, L"%x", toGuiString(n));
}


template <class T> inline
std::wstring translate(const std::wstring& singular, const std::wstring& plural, T n)
{
    static_assert(sizeof(n) <= sizeof(std::int64_t), "");
    return translate(singular, plural, static_cast<std::int64_t>(n));
}


inline
const TranslationHandler*& getTranslationInstance()
{
    //avoid static destruction order fiasco: there may be accesses to "getTranslator()" during process shutdown e.g. show message in debug_minidump.cpp!
    //=> use POD instead of a std::unique_ptr<>!!!
    static const TranslationHandler* inst = nullptr; //external linkage even in header!
    return inst;
}


struct CleanUpTranslationHandler
{
    ~CleanUpTranslationHandler()
    {
        const TranslationHandler*& handler = getTranslationInstance();
        assert(!handler); //clean up at a better time rather than during static destruction! potential MT issues!?
        delete handler;
        handler = nullptr; //getTranslator() may be called even after static objects of this translation unit are destroyed!
    }
};
}


inline
void setTranslator(std::unique_ptr<const TranslationHandler>&& newHandler)
{
    static implementation::CleanUpTranslationHandler cuth; //external linkage even in header!

    const TranslationHandler*& handler = implementation::getTranslationInstance();
    delete handler;
    handler = newHandler.release();
}


inline
const TranslationHandler* getTranslator() { return implementation::getTranslationInstance(); }
}

#endif //I18_N_H_3843489325044253425456
bgstack15