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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
// **************************************************************************
// * 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
std::shared_ptr<const TranslationHandler> getTranslator();
//######################## implementation ##############################
namespace implementation
{
inline
std::wstring translate(const std::wstring& text)
{
if (std::shared_ptr<const TranslationHandler> t = getTranslator()) //std::shared_ptr => temporarily take (shared) ownership while using the interface!
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 (std::shared_ptr<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
std::shared_ptr<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 or some detached thread assembling an error message!
//=> use POD instead of a plain std::shared_ptr<>!!!
static std::shared_ptr<const TranslationHandler>* inst = nullptr; //external linkage even in header!
return inst;
}
struct CleanUpTranslationHandler
{
~CleanUpTranslationHandler()
{
std::shared_ptr<const TranslationHandler>*& handler = getTranslationInstance();
assert(!handler); //clean up at a better time rather than during static destruction! MT issues!
delete handler;
handler = nullptr; //getTranslator() may be called even after static objects of this translation unit are destroyed!
}
};
}
//setTranslator/getTranslator() operating on a global are obviously racy for MT usage
//=> make them fast to cover the rare case of a language change and the not-so-rare case of language clean-up during shutdown
//=> can't synchronize with std::mutex which is non-POD and again leads to global destruction order fiasco
//=> return std::shared_ptr to let instance life time be handled by caller (MT!)
inline
void setTranslator(std::unique_ptr<const TranslationHandler>&& newHandler)
{
static implementation::CleanUpTranslationHandler cuth; //external linkage even in header!
std::shared_ptr<const TranslationHandler>*& handler = implementation::getTranslationInstance();
auto tmp = handler;
handler = nullptr;
delete tmp;
if (newHandler)
handler = new std::shared_ptr<const TranslationHandler>(std::move(newHandler));
}
inline
std::shared_ptr<const TranslationHandler> getTranslator()
{
std::shared_ptr<const TranslationHandler>*& handler = implementation::getTranslationInstance();
if (handler)
return *handler;
return nullptr;
}
}
#endif //I18_N_H_3843489325044253425456
|