File: StringTable.h

package info (click to toggle)
freeorion 0.5.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 194,940 kB
  • sloc: cpp: 186,508; python: 40,969; ansic: 1,164; xml: 719; makefile: 32; sh: 7
file content (231 lines) | stat: -rw-r--r-- 8,532 bytes parent folder | download
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
#ifndef StringTable_h_
#define StringTable_h_

//! @file
//!     Declares the StringTable class.

#include <boost/unordered_map.hpp>
#include <string>
#include <set>
#include <mutex>
#include <memory>

//! Provides a key based translation string lookup with fallback.
//!
//! StringTable is a map to look up translations based on key representing a
//! translation entry.  When a translation for a key is not found the
//! StringTable delegates the lookup to another fallback StringTable, which is
//! filled with entries from another language.  When no fallback StringTable
//! contains the requested translation entry an error string is returned,
//! pointing out the missing translation entry.
//!
//! StringTables are loaded from text files which contain the translation
//! entries for a specific language.  Those translation files are named like
//!
//! ```{.txt}
//! <LANG ISO 639-1>.txt
//! ```
//!
//! where <LANG ISO 639-1> is the two letter language identifier for a language
//! according to ISO 639-1 (e.g English=en, Russian=ru, German=de, ...).
//!
//! The format of those translation files consists of newline separated entries
//! where:
//!
//! * The first line is the native language name.
//! * Linse starting with a hash sign `#` are considered comments and are
//!   ignored when loading the file.
//! * Empty lines are ignored when loading the file.
//! * The translation entries consist of an key followed by a newline, followed
//!   by the translated text followed by a newline.
//! * An identifer must be unique within an translation file.
//! * Identifiers consist of the any latin character, digits or an underscore.
//!   Spaces or other characters are not allowed.
//! * The translated text is a valid UTF-8 based string, which will either be
//!   terminated by a newline, trimming of any whitespace at the begining or end
//!   of the translated string or a multi-line string.
//! * A multi-line string starts and ends with three single quotes `'''`. As the
//!   name implies, a multi-line string can span over multiple lines and any
//!   whitespace inside the string will be preseved.
//!
//! A minimal example translation file for the english language `en.txt` should
//! look like:
//!
//! ```{.txt}
//! English
//! # This line is a comment, the line above is the native language name.
//!
//! # The two lines below are an translation entry consisting of an identifer
//! # and a single line of translated text.
//! A_SINGLE_LINE
//! This is the translated text associated with A_SINGLE_LINE
//!
//! # The line below is a translation entry with multiple lines of translated
//! # text.
//! A_MULTI_LINE
//! '''This text
//! spans over
//! multiple lines.
//!     The whitespace before this line is preseved, same goes for the newlines
//! after this line up to the triple single quotes.
//!
//!
//! '''
//!
//! # This translation entry doesn't preserve whitespace
//! SINGLE_LINE_WS_TRIM
//!          This text is intended in the translation file, but the whitespace will be trimmed.
//!
//! # This translation entry preseves whitespace
//! SINGLE_LINE_WS_PRESERVE
//! '''      This text will keep its leading and trailing whitespace   '''
//! ```
//!
//! StringTables implement multiple string substitution mechanism.
//!
//! The first substitution mechanism replaces references to a translation entry
//! with the text of another translation entry.  The syntax for a reference
//! consist of two opening square brackets `[[`, the referenced key and two
//! closing brackets `]]`.  For example:
//!
//! ```{.txt}
//! REFERENCE
//! I am the replaced text
//!
//! TRANSLATION_ENTRY
//! This translation embeds another translation right here > [[REFERENCE]]
//! ```
//!
//! would return
//!
//! ```{.txt}
//! This translation embeds another translaion right here > I am the replaced text
//! ```
//!
//! The second substitution mechanism replaces typed references with type links
//! and the translation text.  The syntax for a typed reference consists of two
//! opening square brackets `[[`, a type key, a space, the referenced key and
//! two closing brackets `]]``.  For example:
//!
//! ```{.txt}
//! REFERENCE
//! I am referenced
//!
//! TRANSLATION_ENTRY
//! This translation links to [[foo_type REFERENCE]]
//! ```
//!
//! would return
//!
//! ```{.txt}
//! This translation links to <foo_type REFERENCE>I am referenced</foo_type>
//! ```
class StringTable {
public:
    //! Create an empty StringTable
    StringTable() = default;

    //! Create a StringTable from the given @p filename.
    //!
    //! @param  filename
    //!     The file name of the translation file to load.
    //! @param  fallback
    //!     A StringTable that should be used look up unknown translation
    //!     entries.
    explicit StringTable(std::string filename, std::shared_ptr<const StringTable> fallback = nullptr);

    ~StringTable() = default;

    //! Returns if a translation for @p key exists. Caller should have shared (read)
    //! access to this stringtable before calling this function.
    //!
    //! @param key
    //!     The identifying key of a translation entry.
    //!
    //! @return
    //!     True iff a translation with that key exists, false otherwise.
    [[nodiscard]] bool StringExists(const std::string& key) const;
    [[nodiscard]] bool StringExists(const std::string_view key) const;
    [[nodiscard]] bool StringExists(const char* key) const;

    //! Returns if a translation for @p key exists and what that translation is, if it exists.
    //! Caller should have shared (read) access to this stringtable before calling this function.
    //!
    //! @param key
    //!     The identifying key of a translation entry.
    //!
    //! @return
    //!     pair containing true iff a translation with that key exists, false otherwise, and
    //!                     reference to the translation or to an emptry string if no translation exists
    [[nodiscard]] std::pair<bool, const std::string&> CheckGet(const std::string& key) const;
    [[nodiscard]] std::pair<bool, const std::string&> CheckGet(const std::string_view key) const;
    [[nodiscard]] std::pair<bool, const std::string&> CheckGet(const char* key) const;

    //! Returns the native language name of this StringTable.
    [[nodiscard]] const std::string& Language() const noexcept { return m_language; }

    //! Returns the translation file name this StringTable was loaded from.
    [[nodiscard]] const std::string& Filename() const noexcept { return m_filename; }

    [[nodiscard]] const auto& AllStrings() const noexcept { return m_strings; }

    //! Adds the a @p key and @p value pair to this StringTable, and returns a reference
    //! to the newly-added string. If the key already exists, it is overwritten.
    //! Caller should have exclusive (write) access to this stringtable before calling
    //! this function.
    //!
    //! @param key
    //!     The identifying key of a translation entry.
    //! @param value
    //!     The value to be stored with index key
    //!
    //! @return
    //!     A string for @p key containing value
    const std::string& Add(std::string key, std::string value);

private:
    //! Loads this StringTable from #m_filename
    //!
    //! @param fallback
    //!     A StringTable that should be used look up unknown translation
    //!     entries.
    void Load(std::shared_ptr<const StringTable> fallback = nullptr);

    //! The filename this StringTable was loaded from.
    std::string m_filename;

    //! The native language name of the StringTable translations.
    std::string m_language;

    struct hasher {
        using is_transparent = void;

        size_t operator()(const auto& key) const
        { return boost::hash_range(key.begin(), key.end()); }

        size_t operator()(const char* key) const {
            const std::string_view sv{key};
            return boost::hash_range(sv.begin(), sv.end());
        }
    };

    struct equalizer {
        using is_transparent = void;

        bool operator()(const auto& lhs, const auto& rhs) const noexcept
        { return lhs.compare(rhs) == 0; }

        bool operator()(const char* lhs, const auto& rhs) const noexcept
        { return rhs.compare(lhs) == 0; }
    };

    //! Mapping of translation entry keys to translated strings.
    boost::unordered_map<std::string, std::string, hasher, equalizer> m_strings;

    //! True if the StringTable was completely loaded and all references
    //! were successfully resolved.
    bool m_initialized = false;
};


#endif