1 #include "../langs/i18n_decls.h"
2 
3 #include "sized_string.h"
4 #include "macros.h"
5 
6 #include <stdint.h>
7 #include <string.h>
8 
9 #ifdef msgid
10 #error "msgid is already defined"
11 #endif
12 #ifdef msgstr
13 #error "msgstr is already defined"
14 #endif
15 #ifdef LANG_POSIX_LOCALE
16 #error "LANG_POSIX_LOCALE is already defined"
17 #endif
18 #ifdef LANG_WINDOWS_ID
19 #error "LANG_WINDOWS_ID is already defined"
20 #endif
21 #ifdef LANG_PRIORITY
22 #error "LANG_PRIORITY is already defined"
23 #endif
24 
25 /***** Parsing localized strings *****/
26 
27 #define msgid(x) curr_id = (STR_##x);
28 #define msgstr(x)                                      \
29     localized_strings[_LANG_ID][curr_id].str    = (x); \
30     localized_strings[_LANG_ID][curr_id].length = sizeof(x) - 1;
31 #define LANG_WINDOWS_ID(x)
32 #define LANG_POSIX_LOCALE(x)
33 #define LANG_PRIORITY(x)
34 
35 static STRING canary = STRING_INIT("BUG. PLEASE REPORT.");
36 
init_strings(STRING (* localized_strings)[NUM_STRS])37 static void init_strings(STRING (*localized_strings)[NUM_STRS]) {
38     for (UTOX_LANG i = 0; i < NUM_LANGS; i++) {
39         for (UTOX_I18N_STR j = 0; j < NUM_STRS; j++) {
40             localized_strings[i][j] = canary;
41         }
42     }
43 
44     UTOX_I18N_STR curr_id = 0;
45 
46 #include "ui_i18n.h"
47 }
48 
49 #undef LANG_PRIORITY
50 #undef LANG_POSIX_LOCALE
51 #undef LANG_WINDOWS_ID
52 #undef msgstr
53 #undef msgid
54 
ui_gettext(UTOX_LANG lang,UTOX_I18N_STR string_id)55 STRING *ui_gettext(UTOX_LANG lang, UTOX_I18N_STR string_id) {
56     static STRING localized_strings[NUM_LANGS][NUM_STRS];
57     static int    ready = 0;
58 
59     if (!ready) {
60         init_strings(localized_strings);
61         ready = 1;
62     }
63 
64     if ((lang >= NUM_LANGS) || (string_id >= NUM_STRS)) {
65         return &canary;
66     }
67 
68     return &localized_strings[lang][string_id];
69 }
70 
71 /***** Parsing detection by POSIX locale *****/
72 
73 #define msgid(x)
74 #define msgstr(x)
75 #define LANG_WINDOWS_ID(x)
76 #define LANG_POSIX_LOCALE(x) posix_locales[_LANG_ID] = (x);
77 #define LANG_PRIORITY(x) priorities[_LANG_ID]        = (x);
78 
init_posix_locales(const char * UNUSED (posix_locales[]),int8_t UNUSED (priorities[]))79 static void init_posix_locales(const char *UNUSED(posix_locales[]), int8_t UNUSED(priorities[])) {
80 
81 #include "ui_i18n.h"
82 }
83 
84 #undef LANG_PRIORITY
85 #undef LANG_POSIX_LOCALE
86 #undef LANG_WINDOWS_ID
87 #undef msgstr
88 #undef msgid
89 
ui_guess_lang_by_posix_locale(const char * locale,UTOX_LANG deflt)90 UTOX_LANG ui_guess_lang_by_posix_locale(const char *locale, UTOX_LANG deflt) {
91     static const char *posix_locales[NUM_LANGS];
92     static int8_t      priorities[NUM_LANGS];
93     static int         ready = 0;
94 
95     if (!ready) {
96         init_posix_locales(posix_locales, priorities);
97         ready = 1;
98     }
99 
100     UTOX_LANG found_lang = 0;
101     int8_t    found_prio = INT8_MAX;
102 
103     // Try detecting by full prefix match first.
104     for (UTOX_LANG i = 0; i < NUM_LANGS; i++) {
105         const char *l = posix_locales[i];
106         if (!l) {
107             continue;
108         }
109 
110         if (strstr(locale, l)) {
111             if (found_prio > priorities[i]) {
112                 found_lang = i;
113                 found_prio = priorities[i];
114             }
115         }
116     }
117 
118     if (found_prio < INT8_MAX) {
119         return found_lang;
120     }
121 
122     // It appears we haven't found exact language_territory
123     // match (e.g. zh_TW) for given locale. ,
124     // Try stripping territory off and search only by language part.
125     for (UTOX_LANG i = 0; i < NUM_LANGS; i++) {
126         const char *l = posix_locales[i];
127         if (!l) {
128             continue;
129         }
130 
131         char *sep = strchr(l, '_');
132         if (!sep) {
133             continue;
134         }
135 
136         if (!strncmp(locale, l, sep - l)) {
137             if (found_prio > priorities[i]) {
138                 found_lang = i;
139                 found_prio = priorities[i];
140             }
141         }
142     }
143 
144     return found_prio < INT8_MAX ? found_lang : deflt;
145 }
146 
147 /***** Parsing detection by Windows language id *****/
148 
149 #define msgid(x)
150 #define msgstr(x)
151 #define LANG_WINDOWS_ID(x) windows_lang_ids[_LANG_ID] = (x);
152 #define LANG_POSIX_LOCALE(x)
153 #define LANG_PRIORITY(x) priorities[_LANG_ID] = (x);
154 
init_windows_lang_ids(uint16_t UNUSED (windows_lang_ids[]),int8_t UNUSED (priorities[]))155 static void init_windows_lang_ids(uint16_t UNUSED(windows_lang_ids[]), int8_t UNUSED(priorities[])) {
156 
157 #include "ui_i18n.h"
158 }
159 
160 #undef LANG_PRIORITY
161 #undef LANG_POSIX_LOCALE
162 #undef LANG_WINDOWS_ID
163 #undef msgstr
164 #undef msgid
165 
ui_guess_lang_by_windows_lang_id(uint16_t lang_id,UTOX_LANG deflt)166 UTOX_LANG ui_guess_lang_by_windows_lang_id(uint16_t lang_id, UTOX_LANG deflt) {
167     static uint16_t windows_lang_ids[NUM_LANGS];
168     static int8_t   priorities[NUM_LANGS];
169     static int      ready = 0;
170 
171     if (!ready) {
172         init_windows_lang_ids(windows_lang_ids, priorities);
173         ready = 1;
174     }
175 
176     UTOX_LANG found_lang = 0;
177     int8_t    found_prio = INT8_MAX;
178 
179     // Try detecting by full match first, including sublanguage part.
180     for (UTOX_LANG i = 0; i < NUM_LANGS; i++) {
181         uint16_t l = windows_lang_ids[i];
182         if (!l) {
183             continue;
184         }
185 
186         if (l == lang_id) {
187             if (found_prio > priorities[i]) {
188                 found_lang = i;
189                 found_prio = priorities[i];
190             }
191         }
192     }
193 
194     if (found_prio < INT8_MAX) {
195         return found_lang;
196     }
197 
198     // It appears we haven't found exact id match.
199     // Try matching by the lower 8 bits, which contain language family part.
200     for (UTOX_LANG i = 0; i < NUM_LANGS; i++) {
201         uint16_t l = windows_lang_ids[i];
202         if (!l) {
203             continue;
204         }
205 
206         if ((l & 0xFF) == (lang_id & 0xFF)) {
207             if (found_prio > priorities[i]) {
208                 found_lang = i;
209                 found_prio = priorities[i];
210             }
211         }
212     }
213 
214     return found_prio < INT8_MAX ? found_lang : deflt;
215 }
216