dinput: SetActionMap setting the device buffer.
[wine] / tools / wrc / po.c
1 /*
2  * Support for po files
3  *
4  * Copyright 2010 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <assert.h>
28 #include <ctype.h>
29 #ifdef HAVE_GETTEXT_PO_H
30 #include <gettext-po.h>
31 #endif
32
33 #include "wrc.h"
34 #include "genres.h"
35 #include "newstruc.h"
36 #include "utils.h"
37 #include "windef.h"
38 #include "winbase.h"
39 #include "wingdi.h"
40 #include "winuser.h"
41 #include "wine/list.h"
42 #include "wine/unicode.h"
43
44 #ifdef HAVE_LIBGETTEXTPO
45
46 static const struct
47 {
48     unsigned int id, sub;
49     const char *name;
50 } languages[] =
51 {
52     { LANG_ARABIC,         SUBLANG_NEUTRAL,                     "ar" },
53     { LANG_ARABIC,         SUBLANG_ARABIC_SAUDI_ARABIA,         "ar_SA" },
54     { LANG_ARABIC,         SUBLANG_ARABIC_IRAQ,                 "ar_IQ" },
55     { LANG_ARABIC,         SUBLANG_ARABIC_EGYPT,                "ar_EG" },
56     { LANG_ARABIC,         SUBLANG_ARABIC_LIBYA,                "ar_LY" },
57     { LANG_ARABIC,         SUBLANG_ARABIC_ALGERIA,              "ar_DZ" },
58     { LANG_ARABIC,         SUBLANG_ARABIC_MOROCCO,              "ar_MA" },
59     { LANG_ARABIC,         SUBLANG_ARABIC_TUNISIA,              "ar_TN" },
60     { LANG_ARABIC,         SUBLANG_ARABIC_OMAN,                 "ar_OM" },
61     { LANG_ARABIC,         SUBLANG_ARABIC_YEMEN,                "ar_YE" },
62     { LANG_ARABIC,         SUBLANG_ARABIC_SYRIA,                "ar_SY" },
63     { LANG_ARABIC,         SUBLANG_ARABIC_JORDAN,               "ar_JO" },
64     { LANG_ARABIC,         SUBLANG_ARABIC_LEBANON,              "ar_LB" },
65     { LANG_ARABIC,         SUBLANG_ARABIC_KUWAIT,               "ar_KW" },
66     { LANG_ARABIC,         SUBLANG_ARABIC_UAE,                  "ar_AE" },
67     { LANG_ARABIC,         SUBLANG_ARABIC_BAHRAIN,              "ar_BH" },
68     { LANG_ARABIC,         SUBLANG_ARABIC_QATAR,                "ar_QA" },
69     { LANG_BULGARIAN,      SUBLANG_NEUTRAL,                     "bg" },
70     { LANG_BULGARIAN,      SUBLANG_BULGARIAN_BULGARIA,          "bg_BG" },
71     { LANG_CATALAN,        SUBLANG_NEUTRAL,                     "ca" },
72     { LANG_CATALAN,        SUBLANG_CATALAN_CATALAN,             "ca_ES" },
73     { LANG_CHINESE,        SUBLANG_NEUTRAL,                     "zh" },
74     { LANG_CHINESE,        SUBLANG_CHINESE_TRADITIONAL,         "zh_TW" },
75     { LANG_CHINESE,        SUBLANG_CHINESE_SIMPLIFIED,          "zh_CN" },
76     { LANG_CHINESE,        SUBLANG_CHINESE_HONGKONG,            "zh_HK" },
77     { LANG_CHINESE,        SUBLANG_CHINESE_SINGAPORE,           "zh_SG" },
78     { LANG_CHINESE,        SUBLANG_CHINESE_MACAU,               "zh_MO" },
79     { LANG_CZECH,          SUBLANG_NEUTRAL,                     "cs" },
80     { LANG_CZECH,          SUBLANG_CZECH_CZECH_REPUBLIC,        "cs_CZ" },
81     { LANG_DANISH,         SUBLANG_NEUTRAL,                     "da" },
82     { LANG_DANISH,         SUBLANG_DANISH_DENMARK,              "da_DK" },
83     { LANG_GERMAN,         SUBLANG_NEUTRAL,                     "de" },
84     { LANG_GERMAN,         SUBLANG_GERMAN,                      "de_DE" },
85     { LANG_GERMAN,         SUBLANG_GERMAN_SWISS,                "de_CH" },
86     { LANG_GERMAN,         SUBLANG_GERMAN_AUSTRIAN,             "de_AT" },
87     { LANG_GERMAN,         SUBLANG_GERMAN_LUXEMBOURG,           "de_LU" },
88     { LANG_GERMAN,         SUBLANG_GERMAN_LIECHTENSTEIN,        "de_LI" },
89     { LANG_GREEK,          SUBLANG_NEUTRAL,                     "el" },
90     { LANG_GREEK,          SUBLANG_GREEK_GREECE,                "el_GR" },
91     { LANG_ENGLISH,        SUBLANG_NEUTRAL,                     "en" },
92     { LANG_ENGLISH,        SUBLANG_ENGLISH_US,                  "en_US" },
93     { LANG_ENGLISH,        SUBLANG_ENGLISH_UK,                  "en_GB" },
94     { LANG_ENGLISH,        SUBLANG_ENGLISH_AUS,                 "en_AU" },
95     { LANG_ENGLISH,        SUBLANG_ENGLISH_CAN,                 "en_CA" },
96     { LANG_ENGLISH,        SUBLANG_ENGLISH_NZ,                  "en_NZ" },
97     { LANG_ENGLISH,        SUBLANG_ENGLISH_EIRE,                "en_IE" },
98     { LANG_ENGLISH,        SUBLANG_ENGLISH_SOUTH_AFRICA,        "en_ZA" },
99     { LANG_ENGLISH,        SUBLANG_ENGLISH_JAMAICA,             "en_JM" },
100     { LANG_ENGLISH,        SUBLANG_ENGLISH_CARIBBEAN,           "en_CB" },
101     { LANG_ENGLISH,        SUBLANG_ENGLISH_BELIZE,              "en_BZ" },
102     { LANG_ENGLISH,        SUBLANG_ENGLISH_TRINIDAD,            "en_TT" },
103     { LANG_ENGLISH,        SUBLANG_ENGLISH_ZIMBABWE,            "en_ZW" },
104     { LANG_ENGLISH,        SUBLANG_ENGLISH_PHILIPPINES,         "en_PH" },
105     { LANG_SPANISH,        SUBLANG_NEUTRAL,                     "es" },
106     { LANG_SPANISH,        SUBLANG_SPANISH,                     "es_ES" },
107     { LANG_SPANISH,        SUBLANG_SPANISH_MEXICAN,             "es_MX" },
108     { LANG_SPANISH,        SUBLANG_SPANISH_MODERN,              "es_ES_modern" },
109     { LANG_SPANISH,        SUBLANG_SPANISH_GUATEMALA,           "es_GT" },
110     { LANG_SPANISH,        SUBLANG_SPANISH_COSTA_RICA,          "es_CR" },
111     { LANG_SPANISH,        SUBLANG_SPANISH_PANAMA,              "es_PA" },
112     { LANG_SPANISH,        SUBLANG_SPANISH_DOMINICAN_REPUBLIC,  "es_DO" },
113     { LANG_SPANISH,        SUBLANG_SPANISH_VENEZUELA,           "es_VE" },
114     { LANG_SPANISH,        SUBLANG_SPANISH_COLOMBIA,            "es_CO" },
115     { LANG_SPANISH,        SUBLANG_SPANISH_PERU,                "es_PE" },
116     { LANG_SPANISH,        SUBLANG_SPANISH_ARGENTINA,           "es_AR" },
117     { LANG_SPANISH,        SUBLANG_SPANISH_ECUADOR,             "es_EC" },
118     { LANG_SPANISH,        SUBLANG_SPANISH_CHILE,               "es_CL" },
119     { LANG_SPANISH,        SUBLANG_SPANISH_URUGUAY,             "es_UY" },
120     { LANG_SPANISH,        SUBLANG_SPANISH_PARAGUAY,            "es_PY" },
121     { LANG_SPANISH,        SUBLANG_SPANISH_BOLIVIA,             "es_BO" },
122     { LANG_SPANISH,        SUBLANG_SPANISH_EL_SALVADOR,         "es_SV" },
123     { LANG_SPANISH,        SUBLANG_SPANISH_HONDURAS,            "es_HN" },
124     { LANG_SPANISH,        SUBLANG_SPANISH_NICARAGUA,           "es_NI" },
125     { LANG_SPANISH,        SUBLANG_SPANISH_PUERTO_RICO,         "es_PR" },
126     { LANG_FINNISH,        SUBLANG_NEUTRAL,                     "fi" },
127     { LANG_FINNISH,        SUBLANG_FINNISH_FINLAND,             "fi_FI" },
128     { LANG_FRENCH,         SUBLANG_NEUTRAL,                     "fr" },
129     { LANG_FRENCH,         SUBLANG_FRENCH,                      "fr_FR" },
130     { LANG_FRENCH,         SUBLANG_FRENCH_BELGIAN,              "fr_BE" },
131     { LANG_FRENCH,         SUBLANG_FRENCH_CANADIAN,             "fr_CA" },
132     { LANG_FRENCH,         SUBLANG_FRENCH_SWISS,                "fr_CH" },
133     { LANG_FRENCH,         SUBLANG_FRENCH_LUXEMBOURG,           "fr_LU" },
134     { LANG_FRENCH,         SUBLANG_FRENCH_MONACO,               "fr_MC" },
135     { LANG_HEBREW,         SUBLANG_NEUTRAL,                     "he" },
136     { LANG_HEBREW,         SUBLANG_HEBREW_ISRAEL,               "he_IL" },
137     { LANG_HUNGARIAN,      SUBLANG_NEUTRAL,                     "hu" },
138     { LANG_HUNGARIAN,      SUBLANG_HUNGARIAN_HUNGARY,           "hu_HU" },
139     { LANG_ICELANDIC,      SUBLANG_NEUTRAL,                     "is" },
140     { LANG_ICELANDIC,      SUBLANG_ICELANDIC_ICELAND,           "is_IS" },
141     { LANG_ITALIAN,        SUBLANG_NEUTRAL,                     "it" },
142     { LANG_ITALIAN,        SUBLANG_ITALIAN,                     "it_IT" },
143     { LANG_ITALIAN,        SUBLANG_ITALIAN_SWISS,               "it_CH" },
144     { LANG_JAPANESE,       SUBLANG_NEUTRAL,                     "ja" },
145     { LANG_JAPANESE,       SUBLANG_JAPANESE_JAPAN,              "ja_JP" },
146     { LANG_KOREAN,         SUBLANG_NEUTRAL,                     "ko" },
147     { LANG_KOREAN,         SUBLANG_KOREAN,                      "ko_KR" },
148     { LANG_DUTCH,          SUBLANG_NEUTRAL,                     "nl" },
149     { LANG_DUTCH,          SUBLANG_DUTCH,                       "nl_NL" },
150     { LANG_DUTCH,          SUBLANG_DUTCH_BELGIAN,               "nl_BE" },
151     { LANG_DUTCH,          SUBLANG_DUTCH_SURINAM,               "nl_SR" },
152     { LANG_NORWEGIAN,      SUBLANG_NORWEGIAN_BOKMAL,            "nb_NO" },
153     { LANG_NORWEGIAN,      SUBLANG_NORWEGIAN_NYNORSK,           "nn_NO" },
154     { LANG_POLISH,         SUBLANG_NEUTRAL,                     "pl" },
155     { LANG_POLISH,         SUBLANG_POLISH_POLAND,               "pl_PL" },
156     { LANG_PORTUGUESE,     SUBLANG_NEUTRAL,                     "pt" },
157     { LANG_PORTUGUESE,     SUBLANG_PORTUGUESE_BRAZILIAN,        "pt_BR" },
158     { LANG_PORTUGUESE,     SUBLANG_PORTUGUESE_PORTUGAL,         "pt_PT" },
159     { LANG_ROMANSH,        SUBLANG_NEUTRAL,                     "rm" },
160     { LANG_ROMANSH,        SUBLANG_ROMANSH_SWITZERLAND,         "rm_CH" },
161     { LANG_ROMANIAN,       SUBLANG_NEUTRAL,                     "ro" },
162     { LANG_ROMANIAN,       SUBLANG_ROMANIAN_ROMANIA,            "ro_RO" },
163     { LANG_RUSSIAN,        SUBLANG_NEUTRAL,                     "ru" },
164     { LANG_RUSSIAN,        SUBLANG_RUSSIAN_RUSSIA,              "ru_RU" },
165     { LANG_SERBIAN,        SUBLANG_NEUTRAL,                     "hr" },
166     { LANG_SERBIAN,        SUBLANG_SERBIAN_CROATIA,             "hr_HR" },
167     { LANG_SERBIAN,        SUBLANG_SERBIAN_LATIN,               "sr_RS@latin" },
168     { LANG_SERBIAN,        SUBLANG_SERBIAN_CYRILLIC,            "sr_RS@cyrillic" },
169     { LANG_SLOVAK,         SUBLANG_NEUTRAL,                     "sk" },
170     { LANG_SLOVAK,         SUBLANG_SLOVAK_SLOVAKIA,             "sk_SK" },
171     { LANG_ALBANIAN,       SUBLANG_NEUTRAL,                     "sq" },
172     { LANG_ALBANIAN,       SUBLANG_ALBANIAN_ALBANIA,            "sq_AL" },
173     { LANG_SWEDISH,        SUBLANG_NEUTRAL,                     "sv" },
174     { LANG_SWEDISH,        SUBLANG_SWEDISH_SWEDEN,              "sv_SE" },
175     { LANG_SWEDISH,        SUBLANG_SWEDISH_FINLAND,             "sv_FI" },
176     { LANG_THAI,           SUBLANG_NEUTRAL,                     "th" },
177     { LANG_THAI,           SUBLANG_THAI_THAILAND,               "th_TH" },
178     { LANG_TURKISH,        SUBLANG_NEUTRAL,                     "tr" },
179     { LANG_TURKISH,        SUBLANG_TURKISH_TURKEY,              "tr_TR" },
180     { LANG_URDU,           SUBLANG_NEUTRAL,                     "ur" },
181     { LANG_URDU,           SUBLANG_URDU_PAKISTAN,               "ur_PK" },
182     { LANG_INDONESIAN,     SUBLANG_NEUTRAL,                     "id" },
183     { LANG_INDONESIAN,     SUBLANG_INDONESIAN_INDONESIA,        "id_ID" },
184     { LANG_UKRAINIAN,      SUBLANG_NEUTRAL,                     "uk" },
185     { LANG_UKRAINIAN,      SUBLANG_UKRAINIAN_UKRAINE,           "uk_UA" },
186     { LANG_BELARUSIAN,     SUBLANG_NEUTRAL,                     "be" },
187     { LANG_BELARUSIAN,     SUBLANG_BELARUSIAN_BELARUS,          "be_BY" },
188     { LANG_SLOVENIAN,      SUBLANG_NEUTRAL,                     "sl" },
189     { LANG_SLOVENIAN,      SUBLANG_SLOVENIAN_SLOVENIA,          "sl_SI" },
190     { LANG_ESTONIAN,       SUBLANG_NEUTRAL,                     "et" },
191     { LANG_ESTONIAN,       SUBLANG_ESTONIAN_ESTONIA,            "et_EE" },
192     { LANG_LATVIAN,        SUBLANG_NEUTRAL,                     "lv" },
193     { LANG_LATVIAN,        SUBLANG_LATVIAN_LATVIA,              "lv_LV" },
194     { LANG_LITHUANIAN,     SUBLANG_NEUTRAL,                     "lt" },
195     { LANG_LITHUANIAN,     SUBLANG_LITHUANIAN_LITHUANIA,        "lt_LT" },
196     { LANG_PERSIAN,        SUBLANG_NEUTRAL,                     "fa" },
197     { LANG_PERSIAN,        SUBLANG_PERSIAN_IRAN,                "fa_IR" },
198     { LANG_ARMENIAN,       SUBLANG_NEUTRAL,                     "hy" },
199     { LANG_ARMENIAN,       SUBLANG_ARMENIAN_ARMENIA,            "hy_AM" },
200     { LANG_AZERI,          SUBLANG_NEUTRAL,                     "az" },
201     { LANG_AZERI,          SUBLANG_AZERI_LATIN,                 "az_AZ@latin" },
202     { LANG_AZERI,          SUBLANG_AZERI_CYRILLIC,              "az_AZ@cyrillic" },
203     { LANG_BASQUE,         SUBLANG_NEUTRAL,                     "eu" },
204     { LANG_BASQUE,         SUBLANG_BASQUE_BASQUE,               "eu_ES" },
205     { LANG_MACEDONIAN,     SUBLANG_NEUTRAL,                     "mk" },
206     { LANG_MACEDONIAN,     SUBLANG_MACEDONIAN_MACEDONIA,        "mk_MK" },
207     { LANG_AFRIKAANS,      SUBLANG_NEUTRAL,                     "af" },
208     { LANG_AFRIKAANS,      SUBLANG_AFRIKAANS_SOUTH_AFRICA,      "af_ZA" },
209     { LANG_GEORGIAN,       SUBLANG_NEUTRAL,                     "ka" },
210     { LANG_GEORGIAN,       SUBLANG_GEORGIAN_GEORGIA,            "ka_GE" },
211     { LANG_FAEROESE,       SUBLANG_NEUTRAL,                     "fo" },
212     { LANG_FAEROESE,       SUBLANG_FAEROESE_FAROE_ISLANDS,      "fo_FO" },
213     { LANG_HINDI,          SUBLANG_NEUTRAL,                     "hi" },
214     { LANG_HINDI,          SUBLANG_HINDI_INDIA,                 "hi_IN" },
215     { LANG_MALAY,          SUBLANG_NEUTRAL,                     "ms" },
216     { LANG_MALAY,          SUBLANG_MALAY_MALAYSIA,              "ms_MY" },
217     { LANG_MALAY,          SUBLANG_MALAY_BRUNEI_DARUSSALAM,     "ms_BN" },
218     { LANG_KAZAK,          SUBLANG_NEUTRAL,                     "kk" },
219     { LANG_KAZAK,          SUBLANG_KAZAK_KAZAKHSTAN,            "kk_KZ" },
220     { LANG_KYRGYZ,         SUBLANG_NEUTRAL,                     "ky" },
221     { LANG_KYRGYZ,         SUBLANG_KYRGYZ_KYRGYZSTAN,           "ky_KG" },
222     { LANG_SWAHILI,        SUBLANG_NEUTRAL,                     "sw" },
223     { LANG_SWAHILI,        SUBLANG_SWAHILI_KENYA,               "sw_KE" },
224     { LANG_UZBEK,          SUBLANG_NEUTRAL,                     "uz" },
225     { LANG_UZBEK,          SUBLANG_UZBEK_LATIN,                 "uz_UZ@latin" },
226     { LANG_UZBEK,          SUBLANG_UZBEK_CYRILLIC,              "uz_UZ@cyrillic" },
227     { LANG_TATAR,          SUBLANG_NEUTRAL,                     "tt" },
228     { LANG_TATAR,          SUBLANG_TATAR_RUSSIA,                "tt_TA" },
229     { LANG_PUNJABI,        SUBLANG_NEUTRAL,                     "pa" },
230     { LANG_PUNJABI,        SUBLANG_PUNJABI_INDIA,               "pa_IN" },
231     { LANG_GUJARATI,       SUBLANG_NEUTRAL,                     "gu" },
232     { LANG_GUJARATI,       SUBLANG_GUJARATI_INDIA,              "gu_IN" },
233     { LANG_ORIYA,          SUBLANG_NEUTRAL,                     "or" },
234     { LANG_ORIYA,          SUBLANG_ORIYA_INDIA,                 "or_IN" },
235     { LANG_TAMIL,          SUBLANG_NEUTRAL,                     "ta" },
236     { LANG_TAMIL,          SUBLANG_TAMIL_INDIA,                 "ta_IN" },
237     { LANG_TELUGU,         SUBLANG_NEUTRAL,                     "te" },
238     { LANG_TELUGU,         SUBLANG_TELUGU_INDIA,                "te_IN" },
239     { LANG_KANNADA,        SUBLANG_NEUTRAL,                     "kn" },
240     { LANG_KANNADA,        SUBLANG_KANNADA_INDIA,               "kn_IN" },
241     { LANG_MALAYALAM,      SUBLANG_NEUTRAL,                     "ml" },
242     { LANG_MALAYALAM,      SUBLANG_MALAYALAM_INDIA,             "ml_IN" },
243     { LANG_MARATHI,        SUBLANG_NEUTRAL,                     "mr" },
244     { LANG_MARATHI,        SUBLANG_MARATHI_INDIA,               "mr_IN" },
245     { LANG_SANSKRIT,       SUBLANG_NEUTRAL,                     "sa" },
246     { LANG_SANSKRIT,       SUBLANG_SANSKRIT_INDIA,              "sa_IN" },
247     { LANG_MONGOLIAN,      SUBLANG_NEUTRAL,                     "mn" },
248     { LANG_MONGOLIAN,      SUBLANG_MONGOLIAN_CYRILLIC_MONGOLIA, "mn_MN" },
249     { LANG_WELSH,          SUBLANG_NEUTRAL,                     "cy" },
250     { LANG_WELSH,          SUBLANG_WELSH_UNITED_KINGDOM,        "cy_GB" },
251     { LANG_GALICIAN,       SUBLANG_NEUTRAL,                     "gl" },
252     { LANG_GALICIAN,       SUBLANG_GALICIAN_GALICIAN,           "gl_ES" },
253     { LANG_KONKANI,        SUBLANG_NEUTRAL,                     "kok" },
254     { LANG_KONKANI,        SUBLANG_KONKANI_INDIA,               "kok_IN" },
255     { LANG_DIVEHI,         SUBLANG_NEUTRAL,                     "dv" },
256     { LANG_DIVEHI,         SUBLANG_DIVEHI_MALDIVES,             "dv_MV" },
257     { LANG_BRETON,         SUBLANG_NEUTRAL,                     "br" },
258     { LANG_BRETON,         SUBLANG_BRETON_FRANCE,               "br_FR" },
259
260 #ifdef LANG_ESPERANTO
261     { LANG_ESPERANTO,      SUBLANG_DEFAULT,                     "eo" },
262 #endif
263 #ifdef LANG_WALON
264     { LANG_WALON,          SUBLANG_NEUTRAL,                     "wa" },
265     { LANG_WALON,          SUBLANG_DEFAULT,                     "wa_BE" },
266 #endif
267 #ifdef LANG_CORNISH
268     { LANG_CORNISH,        SUBLANG_NEUTRAL,                     "kw" },
269     { LANG_CORNISH,        SUBLANG_DEFAULT,                     "kw_GB" },
270 #endif
271 #ifdef LANG_GAELIC
272     { LANG_GAELIC,         SUBLANG_NEUTRAL,                     "ga" },
273     { LANG_GAELIC,         SUBLANG_GAELIC,                      "ga_IE" },
274     { LANG_GAELIC,         SUBLANG_GAELIC_SCOTTISH,             "gd_GB" },
275     { LANG_GAELIC,         SUBLANG_GAELIC_MANX,                 "gv_GB" },
276 #endif
277 };
278
279 static void po_xerror( int severity, po_message_t message,
280                        const char *filename, size_t lineno, size_t column,
281                        int multiline_p, const char *message_text )
282 {
283     fprintf( stderr, "%s:%u:%u: %s\n",
284              filename, (unsigned int)lineno, (unsigned int)column, message_text );
285     if (severity) exit(1);
286 }
287
288 static void po_xerror2( int severity, po_message_t message1,
289                         const char *filename1, size_t lineno1, size_t column1,
290                         int multiline_p1, const char *message_text1,
291                         po_message_t message2,
292                         const char *filename2, size_t lineno2, size_t column2,
293                         int multiline_p2, const char *message_text2 )
294 {
295     fprintf( stderr, "%s:%u:%u: %s\n",
296              filename1, (unsigned int)lineno1, (unsigned int)column1, message_text1 );
297     fprintf( stderr, "%s:%u:%u: %s\n",
298              filename2, (unsigned int)lineno2, (unsigned int)column2, message_text2 );
299     if (severity) exit(1);
300 }
301
302 static const struct po_xerror_handler po_xerror_handler = { po_xerror, po_xerror2 };
303
304 static int is_english( const language_t *lan )
305 {
306     return lan->id == LANG_ENGLISH && lan->sub == SUBLANG_DEFAULT;
307 }
308
309 static char *convert_string_utf8( const string_t *str, int codepage )
310 {
311     string_t *newstr = convert_string( str, str_unicode, codepage );
312     char *buffer = xmalloc( newstr->size * 4 + 1 );
313     int len = wine_utf8_wcstombs( 0, newstr->str.wstr, newstr->size, buffer, newstr->size * 4 );
314     buffer[len] = 0;
315     free_string( newstr );
316     return buffer;
317 }
318
319 static char *convert_msgid_ascii( const string_t *str, int error_on_invalid_char )
320 {
321     int i;
322     string_t *newstr = convert_string( str, str_unicode, 1252 );
323     char *buffer = xmalloc( newstr->size + 1 );
324
325     for (i = 0; i < newstr->size; i++)
326     {
327         buffer[i] =  newstr->str.wstr[i];
328         if (newstr->str.wstr[i] >= 32 && newstr->str.wstr[i] <= 127) continue;
329         if (newstr->str.wstr[i] == '\t' || newstr->str.wstr[i] == '\n') continue;
330         if (error_on_invalid_char)
331         {
332             print_location( &newstr->loc );
333             error( "Invalid character %04x in source string\n", newstr->str.wstr[i] );
334         }
335         free( buffer);
336         free_string( newstr );
337         return NULL;
338     }
339     buffer[i] = 0;
340     free_string( newstr );
341     return buffer;
342 }
343
344 static char *get_message_context( char **msgid )
345 {
346     static const char magic[] = "#msgctxt#";
347     char *id, *context;
348
349     if (strncmp( *msgid, magic, sizeof(magic) - 1 )) return NULL;
350     context = *msgid + sizeof(magic) - 1;
351     if (!(id = strchr( context, '#' ))) return NULL;
352     *id = 0;
353     *msgid = id + 1;
354     return context;
355 }
356
357 static po_message_t find_message( po_file_t po, const char *msgid, const char *msgctxt,
358                                   po_message_iterator_t *iterator )
359 {
360     po_message_t msg;
361     const char *context;
362
363     *iterator = po_message_iterator( po, NULL );
364     while ((msg = po_next_message( *iterator )))
365     {
366         if (strcmp( po_message_msgid( msg ), msgid )) continue;
367         if (!msgctxt) break;
368         if (!(context = po_message_msgctxt( msg ))) continue;
369         if (!strcmp( context, msgctxt )) break;
370     }
371     return msg;
372 }
373
374 static void add_po_string( po_file_t po, const string_t *msgid, const string_t *msgstr,
375                            const language_t *lang )
376 {
377     po_message_t msg;
378     po_message_iterator_t iterator;
379     int codepage;
380     char *id, *id_buffer, *context, *str = NULL, *str_buffer = NULL;
381
382     if (!msgid->size) return;
383
384     id_buffer = id = convert_msgid_ascii( msgid, 1 );
385     context = get_message_context( &id );
386
387     if (msgstr)
388     {
389         if (lang) codepage = get_language_codepage( lang->id, lang->sub );
390         else codepage = get_language_codepage( 0, 0 );
391         assert( codepage != -1 );
392         str_buffer = str = convert_string_utf8( msgstr, codepage );
393         if (is_english( lang )) get_message_context( &str );
394     }
395     if (!(msg = find_message( po, id, context, &iterator )))
396     {
397         msg = po_message_create();
398         po_message_set_msgid( msg, id );
399         po_message_set_msgstr( msg, str ? str : "" );
400         if (context) po_message_set_msgctxt( msg, context );
401         po_message_insert( iterator, msg );
402     }
403     if (msgid->loc.file) po_message_add_filepos( msg, msgid->loc.file, msgid->loc.line );
404     po_message_iterator_free( iterator );
405     free( id_buffer );
406     free( str_buffer );
407 }
408
409 struct po_file_lang
410 {
411     struct list entry;
412     language_t  lang;
413     po_file_t   po;
414 };
415
416 static struct list po_file_langs = LIST_INIT( po_file_langs );
417
418 static po_file_t create_po_file(void)
419 {
420     po_file_t po;
421     po_message_t msg;
422     po_message_iterator_t iterator;
423
424     po = po_file_create();
425     iterator = po_message_iterator( po, NULL );
426     msg = po_message_create();
427     po_message_set_msgid( msg, "" );
428     po_message_set_msgstr( msg,
429                            "Project-Id-Version: Wine\n"
430                            "Report-Msgid-Bugs-To: http://bugs.winehq.org\n"
431                            "POT-Creation-Date: N/A\n"
432                            "PO-Revision-Date: N/A\n"
433                            "Last-Translator: Automatically generated\n"
434                            "Language-Team: none\n"
435                            "MIME-Version: 1.0\n"
436                            "Content-Type: text/plain; charset=UTF-8\n"
437                            "Content-Transfer-Encoding: 8bit\n" );
438     po_message_insert( iterator, msg );
439     po_message_iterator_free( iterator );
440     return po;
441 }
442
443 static po_file_t get_po_file( const language_t *lang )
444 {
445     struct po_file_lang *po_file;
446
447     LIST_FOR_EACH_ENTRY( po_file, &po_file_langs, struct po_file_lang, entry )
448         if (po_file->lang.id == lang->id && po_file->lang.sub == lang->sub) return po_file->po;
449
450     /* create a new one */
451     po_file = xmalloc( sizeof(*po_file) );
452     po_file->lang = *lang;
453     po_file->po = create_po_file();
454     list_add_tail( &po_file_langs, &po_file->entry );
455     return po_file->po;
456 }
457
458 static char *get_po_file_name( const language_t *lang )
459 {
460     unsigned int i;
461     char name[40];
462
463     sprintf( name, "%02x-%02x", lang->id, lang->sub );
464     for (i = 0; i < sizeof(languages)/sizeof(languages[0]); i++)
465     {
466         if (languages[i].id == lang->id && languages[i].sub == lang->sub)
467         {
468             strcpy( name, languages[i].name );
469             break;
470         }
471     }
472     strcat( name, ".po" );
473     return xstrdup( name );
474 }
475
476 static unsigned int flush_po_files( const char *output_name )
477 {
478     struct po_file_lang *po_file, *next;
479     unsigned int count = 0;
480
481     LIST_FOR_EACH_ENTRY_SAFE( po_file, next, &po_file_langs, struct po_file_lang, entry )
482     {
483         char *name = get_po_file_name( &po_file->lang );
484         if (output_name)
485         {
486             const char *p = strrchr( output_name, '/' );
487             if (p) p++;
488             else p = output_name;
489             if (!strcmp( p, name ))
490             {
491                 po_file_write( po_file->po, name, &po_xerror_handler );
492                 count++;
493             }
494         }
495         else  /* no specified output name, output a file for every language found */
496         {
497             po_file_write( po_file->po, name, &po_xerror_handler );
498             count++;
499             fprintf( stderr, "created %s\n", name );
500         }
501         po_file_free( po_file->po );
502         list_remove( &po_file->entry );
503         free( po_file );
504         free( name );
505     }
506     return count;
507 }
508
509 static int control_has_title( const control_t *ctrl )
510 {
511     if (!ctrl->title) return 0;
512     if (ctrl->title->type != name_str) return 0;
513     /* check for text static control */
514     if (ctrl->ctlclass && ctrl->ctlclass->type == name_ord && ctrl->ctlclass->name.i_name == CT_STATIC)
515     {
516         switch (ctrl->style->or_mask & SS_TYPEMASK)
517         {
518         case SS_LEFT:
519         case SS_CENTER:
520         case SS_RIGHT:
521             return 1;
522         default:
523             return 0;
524         }
525     }
526     return 1;
527 }
528
529 static void add_pot_stringtable( po_file_t po, const resource_t *res )
530 {
531     const stringtable_t *stt = res->res.stt;
532     int i;
533
534     while (stt)
535     {
536         for (i = 0; i < stt->nentries; i++)
537             if (stt->entries[i].str) add_po_string( po, stt->entries[i].str, NULL, NULL );
538         stt = stt->next;
539     }
540 }
541
542 static void add_po_stringtable( const resource_t *english, const resource_t *res )
543 {
544     const stringtable_t *english_stt = english->res.stt;
545     const stringtable_t *stt = res->res.stt;
546     po_file_t po = get_po_file( stt->lvc.language );
547     int i;
548
549     while (english_stt && stt)
550     {
551         for (i = 0; i < stt->nentries; i++)
552             if (english_stt->entries[i].str && stt->entries[i].str)
553                 add_po_string( po, english_stt->entries[i].str, stt->entries[i].str, stt->lvc.language );
554         stt = stt->next;
555         english_stt = english_stt->next;
556     }
557 }
558
559 static void add_pot_dialog_controls( po_file_t po, const control_t *ctrl )
560 {
561     while (ctrl)
562     {
563         if (control_has_title( ctrl )) add_po_string( po, ctrl->title->name.s_name, NULL, NULL );
564         ctrl = ctrl->next;
565     }
566 }
567
568 static void add_pot_dialog( po_file_t po, const resource_t *res )
569 {
570     const dialog_t *dlg = res->res.dlg;
571
572     if (dlg->title) add_po_string( po, dlg->title, NULL, NULL );
573     if (dlg->font) add_po_string( po, dlg->font->name, NULL, NULL );
574     add_pot_dialog_controls( po, dlg->controls );
575 }
576
577 static void add_po_dialog_controls( po_file_t po, const control_t *english_ctrl,
578                                     const control_t *ctrl, const language_t *lang )
579 {
580     while (english_ctrl && ctrl)
581     {
582         if (control_has_title( english_ctrl ) && control_has_title( ctrl ))
583             add_po_string( po, english_ctrl->title->name.s_name, ctrl->title->name.s_name, lang );
584
585         ctrl = ctrl->next;
586         english_ctrl = english_ctrl->next;
587     }
588 }
589
590 static void add_po_dialog( const resource_t *english, const resource_t *res )
591 {
592     const dialog_t *english_dlg = english->res.dlg;
593     const dialog_t *dlg = res->res.dlg;
594     po_file_t po = get_po_file( dlg->lvc.language );
595
596     if (english_dlg->title && dlg->title)
597         add_po_string( po, english_dlg->title, dlg->title, dlg->lvc.language );
598     if (english_dlg->font && dlg->font)
599         add_po_string( po, english_dlg->font->name, dlg->font->name, dlg->lvc.language );
600     add_po_dialog_controls( po, english_dlg->controls, dlg->controls, dlg->lvc.language );
601 }
602
603 static void add_pot_menu_items( po_file_t po, const menu_item_t *item )
604 {
605     while (item)
606     {
607         if (item->name) add_po_string( po, item->name, NULL, NULL );
608         if (item->popup) add_pot_menu_items( po, item->popup );
609         item = item->next;
610     }
611 }
612
613 static void add_pot_menu( po_file_t po, const resource_t *res )
614 {
615     add_pot_menu_items( po, res->res.men->items );
616 }
617
618 static void add_po_menu_items( po_file_t po, const menu_item_t *english_item,
619                                const menu_item_t *item, const language_t *lang )
620 {
621     while (english_item && item)
622     {
623         if (english_item->name && item->name)
624             add_po_string( po, english_item->name, item->name, lang );
625         if (english_item->popup && item->popup)
626             add_po_menu_items( po, english_item->popup, item->popup, lang );
627         item = item->next;
628         english_item = english_item->next;
629     }
630 }
631
632 static void add_po_menu( const resource_t *english, const resource_t *res )
633 {
634     const menu_item_t *english_items = english->res.men->items;
635     const menu_item_t *items = res->res.men->items;
636     po_file_t po = get_po_file( res->res.men->lvc.language );
637
638     add_po_menu_items( po, english_items, items, res->res.men->lvc.language );
639 }
640
641 static resource_t *find_english_resource( resource_t *res )
642 {
643     resource_t *ptr;
644
645     for (ptr = resource_top; ptr; ptr = ptr->next)
646     {
647         if (ptr->type != res->type) continue;
648         if (!ptr->lan) continue;
649         if (!is_english( ptr->lan )) continue;
650         if (compare_name_id( ptr->name, res->name )) continue;
651         return ptr;
652     }
653     return NULL;
654 }
655
656 void write_pot_file( const char *outname )
657 {
658     resource_t *res;
659     po_file_t po = create_po_file();
660
661     for (res = resource_top; res; res = res->next)
662     {
663         if (!is_english( res->lan )) continue;
664
665         switch (res->type)
666         {
667         case res_acc: break;  /* FIXME */
668         case res_dlg: add_pot_dialog( po, res ); break;
669         case res_men: add_pot_menu( po, res ); break;
670         case res_stt: add_pot_stringtable( po, res ); break;
671         case res_msg: break;  /* FIXME */
672         default: break;
673         }
674     }
675     po_file_write( po, outname, &po_xerror_handler );
676     po_file_free( po );
677 }
678
679 void write_po_files( const char *outname )
680 {
681     resource_t *res, *english;
682     po_file_t po;
683
684     for (res = resource_top; res; res = res->next)
685     {
686         if (!(english = find_english_resource( res ))) continue;
687         po = get_po_file( res->lan );
688
689         switch (res->type)
690         {
691         case res_acc: break;  /* FIXME */
692         case res_dlg: add_po_dialog( english, res ); break;
693         case res_men: add_po_menu( english, res ); break;
694         case res_stt: add_po_stringtable( english, res ); break;
695         case res_msg: break;  /* FIXME */
696         default: break;
697         }
698     }
699     if (!flush_po_files( outname ))
700     {
701         if (outname) error( "No translations found for %s\n", outname );
702         else error( "No translations found\n" );
703     }
704 }
705
706 static resource_t *new_top, *new_tail;
707
708 static version_t *get_dup_version( language_t *lang )
709 {
710     /* English "translations" take precedence over the original rc contents */
711     return new_version( is_english( lang ) ? 1 : -1 );
712 }
713
714 static name_id_t *dup_name_id( name_id_t *id )
715 {
716     name_id_t *new;
717
718     if (!id || id->type != name_str) return id;
719     new = new_name_id();
720     *new = *id;
721     new->name.s_name = convert_string( id->name.s_name, str_unicode, 1252 );
722     return new;
723 }
724
725 static resource_t *dup_resource( resource_t *res, language_t *lang )
726 {
727     resource_t *new = xmalloc( sizeof(*new) );
728
729     *new = *res;
730     new->lan = lang;
731     new->next = new->prev = NULL;
732     new->name = dup_name_id( res->name );
733
734     switch (res->type)
735     {
736     case res_dlg:
737         new->res.dlg = xmalloc( sizeof(*(new)->res.dlg) );
738         *new->res.dlg = *res->res.dlg;
739         new->res.dlg->lvc.language = lang;
740         new->res.dlg->lvc.version = get_dup_version( lang );
741         break;
742     case res_men:
743         new->res.men = xmalloc( sizeof(*(new)->res.men) );
744         *new->res.men = *res->res.men;
745         new->res.men->lvc.language = lang;
746         new->res.men->lvc.version = get_dup_version( lang );
747         break;
748     case res_stt:
749         new->res.stt = xmalloc( sizeof(*(new)->res.stt) );
750         *new->res.stt = *res->res.stt;
751         new->res.stt->lvc.language = lang;
752         new->res.stt->lvc.version = get_dup_version( lang );
753         break;
754     default:
755         assert(0);
756     }
757     return new;
758 }
759
760 static string_t *translate_string( po_file_t po, string_t *str, int *found )
761 {
762     po_message_t msg;
763     po_message_iterator_t iterator;
764     string_t *new;
765     const char *transl;
766     int res;
767     char *buffer, *msgid, *context;
768
769     if (!str->size || !(buffer = convert_msgid_ascii( str, 0 )))
770         return convert_string( str, str_unicode, 1252 );
771
772     msgid = buffer;
773     context = get_message_context( &msgid );
774     msg = find_message( po, msgid, context, &iterator );
775     po_message_iterator_free( iterator );
776
777     if (msg && !po_message_is_fuzzy( msg ))
778     {
779         transl = po_message_msgstr( msg );
780         if (!transl[0]) transl = msgid;  /* ignore empty strings */
781         else (*found)++;
782     }
783     else transl = msgid;
784
785     new = xmalloc( sizeof(*new) );
786     new->type = str_unicode;
787     new->size = wine_utf8_mbstowcs( 0, transl, strlen(transl), NULL, 0 );
788     new->str.wstr = xmalloc( (new->size+1) * sizeof(WCHAR) );
789     res = wine_utf8_mbstowcs( MB_ERR_INVALID_CHARS, transl, strlen(transl), new->str.wstr, new->size );
790     if (res == -2)
791         error( "Invalid utf-8 character in string '%s'\n", transl );
792     new->str.wstr[new->size] = 0;
793     free( buffer );
794     return new;
795 }
796
797 static control_t *translate_controls( po_file_t po, control_t *ctrl, int *found )
798 {
799     control_t *new, *head = NULL, *tail = NULL;
800
801     while (ctrl)
802     {
803         new = xmalloc( sizeof(*new) );
804         *new = *ctrl;
805         if (control_has_title( ctrl ))
806         {
807             new->title = new_name_id();
808             *new->title = *ctrl->title;
809             new->title->name.s_name = translate_string( po, ctrl->title->name.s_name, found );
810         }
811         else new->title = dup_name_id( ctrl->title );
812         new->ctlclass = dup_name_id( ctrl->ctlclass );
813         if (tail) tail->next = new;
814         else head = new;
815         new->next = NULL;
816         new->prev = tail;
817         tail = new;
818         ctrl = ctrl->next;
819     }
820     return head;
821 }
822
823 static menu_item_t *translate_items( po_file_t po, menu_item_t *item, int *found )
824 {
825     menu_item_t *new, *head = NULL, *tail = NULL;
826
827     while (item)
828     {
829         new = xmalloc( sizeof(*new) );
830         *new = *item;
831         if (item->name) new->name = translate_string( po, item->name, found );
832         if (item->popup) new->popup = translate_items( po, item->popup, found );
833         if (tail) tail->next = new;
834         else head = new;
835         new->next = NULL;
836         new->prev = tail;
837         tail = new;
838         item = item->next;
839     }
840     return head;
841 }
842
843 static stringtable_t *translate_stringtable( po_file_t po, stringtable_t *stt,
844                                              language_t *lang, int *found )
845 {
846     stringtable_t *new, *head = NULL, *tail = NULL;
847     int i;
848
849     while (stt)
850     {
851         new = xmalloc( sizeof(*new) );
852         *new = *stt;
853         new->lvc.language = lang;
854         new->lvc.version = get_dup_version( lang );
855         new->entries = xmalloc( new->nentries * sizeof(*new->entries) );
856         memcpy( new->entries, stt->entries, new->nentries * sizeof(*new->entries) );
857         for (i = 0; i < stt->nentries; i++)
858             if (stt->entries[i].str)
859                 new->entries[i].str = translate_string( po, stt->entries[i].str, found );
860
861         if (tail) tail->next = new;
862         else head = new;
863         new->next = NULL;
864         new->prev = tail;
865         tail = new;
866         stt = stt->next;
867     }
868     return head;
869 }
870
871 static void translate_dialog( po_file_t po, dialog_t *dlg, dialog_t *new, int *found )
872 {
873     if (dlg->title) new->title = translate_string( po, dlg->title, found );
874     if (dlg->font)
875     {
876         new->font = xmalloc( sizeof(*dlg->font) );
877         new->font = dlg->font;
878         new->font->name = translate_string( po, dlg->font->name, found );
879     }
880     new->controls = translate_controls( po, dlg->controls, found );
881 }
882
883 static void translate_resources( po_file_t po, language_t *lang )
884 {
885     resource_t *res;
886
887     for (res = resource_top; res; res = res->next)
888     {
889         resource_t *new = NULL;
890         int found = 0;
891
892         if (!is_english( res->lan )) continue;
893
894         switch (res->type)
895         {
896         case res_acc:
897             /* FIXME */
898             break;
899         case res_dlg:
900             new = dup_resource( res, lang );
901             translate_dialog( po, res->res.dlg, new->res.dlg, &found );
902             break;
903         case res_men:
904             new = dup_resource( res, lang );
905             new->res.men->items = translate_items( po, res->res.men->items, &found );
906             break;
907         case res_stt:
908             new = dup_resource( res, lang );
909             new->res.stt = translate_stringtable( po, res->res.stt, lang, &found );
910             break;
911         case res_msg:
912             /* FIXME */
913             break;
914         default:
915             break;
916         }
917
918         if (new && found)
919         {
920             if (new_tail) new_tail->next = new;
921             else new_top = new;
922             new->prev = new_tail;
923             new_tail = new;
924         }
925     }
926 }
927
928 void add_translations( const char *po_dir )
929 {
930     resource_t *res;
931     po_file_t po;
932     char buffer[256];
933     char *p, *tok, *name;
934     unsigned int i;
935     FILE *f;
936
937     /* first check if we have English resources to translate */
938     for (res = resource_top; res; res = res->next) if (is_english( res->lan )) break;
939     if (!res) return;
940
941     new_top = new_tail = NULL;
942
943     name = strmake( "%s/LINGUAS", po_dir );
944     if (!(f = fopen( name, "r" )))
945     {
946         free( name );
947         return;
948     }
949     free( name );
950     while (fgets( buffer, sizeof(buffer), f ))
951     {
952         if ((p = strchr( buffer, '#' ))) *p = 0;
953         for (tok = strtok( buffer, " \t\r\n" ); tok; tok = strtok( NULL, " \t\r\n" ))
954         {
955             for (i = 0; i < sizeof(languages)/sizeof(languages[0]); i++)
956                 if (!strcmp( tok, languages[i].name )) break;
957
958             if (i == sizeof(languages)/sizeof(languages[0]))
959                 error( "unknown language '%s'\n", tok );
960
961             name = strmake( "%s/%s.po", po_dir, tok );
962             if (!(po = po_file_read( name, &po_xerror_handler )))
963                 error( "cannot load po file for language '%s'\n", tok );
964             translate_resources( po, new_language(languages[i].id, languages[i].sub) );
965             po_file_free( po );
966             free( name );
967         }
968     }
969     fclose( f );
970
971     /* prepend the translated resources to the global list */
972     if (new_tail)
973     {
974         new_tail->next = resource_top;
975         resource_top->prev = new_tail;
976         resource_top = new_top;
977     }
978 }
979
980 #else  /* HAVE_LIBGETTEXTPO */
981
982 void write_pot_file( const char *outname )
983 {
984     error( "PO files not supported in this wrc build\n" );
985 }
986
987 void write_po_files( const char *outname )
988 {
989     error( "PO files not supported in this wrc build\n" );
990 }
991
992 void add_translations( const char *po_dir )
993 {
994 }
995
996 #endif