1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 /* Currently the "all" installer has a bit over 100 UI languages, and
21 * I doubt it will grow a lot over that.
23 #define MAX_LANGUAGES 200
25 #define WIN32_LEAN_AND_MEAN
35 #include <sal/macros.h>
36 #include <systools/win32/uwinapi.h>
39 #include <spellchecker_selection.hxx>
41 static bool GetMsiPropA( MSIHANDLE hMSI
, const char* pPropName
, char** ppValue
)
44 if ( MsiGetPropertyA( hMSI
, pPropName
, const_cast<char *>(""), &sz
) == ERROR_MORE_DATA
) {
46 DWORD nbytes
= sz
* sizeof( char );
47 char* buff
= static_cast<char*>( malloc( nbytes
) );
48 assert(buff
); // Don't handle OOM conditions
49 ZeroMemory( buff
, nbytes
);
50 MsiGetPropertyA( hMSI
, pPropName
, buff
, &sz
);
52 return ( strlen(buff
) > 0 );
58 langid_to_string( LANGID langid
)
60 /* Map from LANGID to string. The languages below are now in
61 * alphabetical order of codes as in
62 * l10ntools/source/ulfconv/msi-encodinglist.txt. Only the
63 * language part is returned in the string.
65 switch (PRIMARYLANGID (langid
)) {
66 case LANG_AFRIKAANS
: return "af";
67 case LANG_AMHARIC
: return "am";
68 case LANG_ARABIC
: return "ar";
69 case LANG_ASSAMESE
: return "as";
70 case LANG_BELARUSIAN
: return "be";
71 case LANG_BULGARIAN
: return "bg";
72 case LANG_BENGALI
: return "bn";
73 case LANG_BRETON
: return "br";
74 case LANG_CATALAN
: return "ca";
75 case LANG_CZECH
: return "cs";
76 case LANG_WELSH
: return "cy";
77 case LANG_DANISH
: return "da";
78 case LANG_GERMAN
: return "de";
79 case LANG_GREEK
: return "el";
80 case LANG_ENGLISH
: return "en";
81 case LANG_SPANISH
: return "es";
82 case LANG_ESTONIAN
: return "et";
83 case LANG_BASQUE
: return "eu";
84 case LANG_FARSI
: return "fa";
85 case LANG_FINNISH
: return "fi";
86 case LANG_FAEROESE
: return "fo";
87 case LANG_FRENCH
: return "fr";
88 case LANG_IRISH
: return "ga";
89 case LANG_GALICIAN
: return "gl";
90 case LANG_GUJARATI
: return "gu";
91 case LANG_HEBREW
: return "he";
92 case LANG_HINDI
: return "hi";
93 case LANG_HUNGARIAN
: return "hu";
94 case LANG_ARMENIAN
: return "hy";
95 case LANG_INDONESIAN
: return "id";
96 case LANG_ICELANDIC
: return "is";
97 case LANG_ITALIAN
: return "it";
98 case LANG_JAPANESE
: return "ja";
99 case LANG_GEORGIAN
: return "ka";
100 case LANG_KAZAK
: return "kk";
101 case LANG_KHMER
: return "km";
102 case LANG_KANNADA
: return "kn";
103 case LANG_KOREAN
: return "ko";
104 case LANG_KASHMIRI
: return "ks";
105 case LANG_LAO
: return "lo";
106 case LANG_LITHUANIAN
: return "lt";
107 case LANG_LATVIAN
: return "lv";
108 case LANG_MACEDONIAN
: return "mk";
109 case LANG_MALAYALAM
: return "ml";
110 case LANG_MONGOLIAN
: return "mn";
111 case LANG_MARATHI
: return "mr";
112 case LANG_MALAY
: return "ms";
113 case LANG_MALTESE
: return "mt";
114 case LANG_NEPALI
: return "ne";
115 case LANG_DUTCH
: return "nl";
116 case LANG_SOTHO
: return "ns";
117 case LANG_ORIYA
: return "or";
118 case LANG_PUNJABI
: return "pa";
119 case LANG_POLISH
: return "pl";
120 case LANG_PORTUGUESE
: return "pt";
121 case LANG_ROMANSH
: return "rm";
122 case LANG_ROMANIAN
: return "ro";
123 case LANG_RUSSIAN
: return "ru";
124 case LANG_KINYARWANDA
: return "rw";
125 case LANG_SANSKRIT
: return "sa";
126 case LANG_UPPER_SORBIAN
: return "sb";
127 case LANG_SINDHI
: return "sd";
128 case LANG_SLOVAK
: return "sk";
129 case LANG_SLOVENIAN
: return "sl";
130 case LANG_ALBANIAN
: return "sq";
131 case LANG_SWEDISH
: return "sv";
132 case LANG_SWAHILI
: return "sw";
133 case LANG_TAMIL
: return "ta";
134 case LANG_TELUGU
: return "te";
135 case LANG_TAJIK
: return "tg";
136 case LANG_THAI
: return "th";
137 case LANG_TIGRIGNA
: return "ti";
138 case LANG_TSWANA
: return "tn";
139 case LANG_TURKISH
: return "tr";
140 case LANG_TATAR
: return "tt";
141 case LANG_UKRAINIAN
: return "uk";
142 case LANG_URDU
: return "ur";
143 case LANG_UZBEK
: return "uz";
144 case LANG_VIETNAMESE
: return "vi";
145 case LANG_XHOSA
: return "xh";
146 case LANG_CHINESE
: return "zh";
147 case LANG_ZULU
: return "zu";
151 case MAKELANGID(LANG_SERBIAN
, 0x05): return "bs";
152 case MAKELANGID(LANG_SERBIAN
, SUBLANG_DEFAULT
): return "hr";
153 case MAKELANGID(LANG_NORWEGIAN
, SUBLANG_NORWEGIAN_BOKMAL
): return "nb";
154 case MAKELANGID(LANG_NORWEGIAN
, SUBLANG_NORWEGIAN_NYNORSK
): return "nn";
155 case MAKELANGID(LANG_SERBIAN
, SUBLANG_SERBIAN_LATIN
): return "sh";
156 case MAKELANGID(LANG_SERBIAN
, SUBLANG_SERBIAN_CYRILLIC
): return "sr";
157 default: return nullptr;
162 /* Here we collect the UI languages present on the system;
163 * MAX_LANGUAGES is certainly enough for that
165 static const char *ui_langs
[MAX_LANGUAGES
];
166 static int num_ui_langs
= 0;
168 static void add_ui_lang(char const * lang
)
170 if (lang
!= nullptr && num_ui_langs
!= SAL_N_ELEMENTS(ui_langs
)) {
171 ui_langs
[num_ui_langs
++] = lang
;
176 enum_ui_lang_proc (LPTSTR language
, LONG_PTR
/* unused_lParam */)
178 long langid
= strtol(language
, nullptr, 16);
181 add_ui_lang(langid_to_string(static_cast<LANGID
>(langid
)));
182 if (num_ui_langs
== SAL_N_ELEMENTS(ui_langs
) )
188 present_in_ui_langs(const char *lang
)
190 for (int i
= 0; i
< num_ui_langs
; i
++)
192 if (strchr (lang
, '_') != nullptr)
193 if (memcmp (ui_langs
[i
], lang
, std::min(strlen(ui_langs
[i
]), strlen(lang
))) == 0)
195 if (strcmp (ui_langs
[i
], lang
) == 0)
203 /* TODO-BCP47: unlimit this, and if possible change from '_' to '-' separator
205 // XXX NOTE: the sizeof needs to follow what is accepted in
206 // setup_native/source/packinfo/spellchecker_selection.pl
207 struct InstallLocalized
{
208 char lang
[sizeof("lll_Ssss_CC_vvvvvvvv")];
212 void addMatchingDictionaries(
213 char const * lang
, InstallLocalized
* dicts
, int ndicts
)
215 for (int i
= 0; i
!= SAL_N_ELEMENTS(setup_native::languageDictionaries
);
218 if (strcmp(lang
, setup_native::languageDictionaries
[i
].language
) == 0) {
219 for (char const * const * p
= setup_native::languageDictionaries
[i
].
223 for (int j
= 0; j
!= ndicts
; ++j
) {
224 if (_stricmp(*p
, dicts
[j
].lang
) == 0) {
225 dicts
[j
].install
= true;
237 extern "C" __declspec(dllexport
) UINT __stdcall
SelectLanguage( MSIHANDLE handle
)
240 MSIHANDLE database
, view
, record
;
243 InstallLocalized langs
[MAX_LANGUAGES
];
245 InstallLocalized dicts
[MAX_LANGUAGES
];
247 database
= MsiGetActiveDatabase(handle
);
249 if (MsiDatabaseOpenViewA(database
, "SELECT Feature from Feature WHERE Feature_Parent = 'gm_Langpack_Languageroot'", &view
) != ERROR_SUCCESS
) {
250 MsiCloseHandle(database
);
251 return ERROR_SUCCESS
;
254 if (MsiViewExecute(view
, NULL
) != ERROR_SUCCESS
) {
255 MsiCloseHandle(view
);
256 MsiCloseHandle(database
);
257 return ERROR_SUCCESS
;
260 while (nlangs
< MAX_LANGUAGES
&&
261 MsiViewFetch(view
, &record
) == ERROR_SUCCESS
) {
262 length
= sizeof(feature
);
263 if (MsiRecordGetStringA(record
, 1, feature
, &length
) != ERROR_SUCCESS
) {
264 MsiCloseHandle(record
);
265 MsiCloseHandle(view
);
266 MsiCloseHandle(database
);
267 return ERROR_SUCCESS
;
270 /* Keep track of what langpacks are included in this installer.
272 strcpy(langs
[nlangs
].lang
, feature
+ strlen("gm_Langpack_r_"));
273 langs
[nlangs
].install
= false;
276 MsiCloseHandle(record
);
279 MsiCloseHandle(view
);
281 /* Keep track of what dictionaries are included in this installer:
283 if (MsiDatabaseOpenViewA(
285 ("SELECT Feature from Feature WHERE"
286 " Feature_Parent = 'gm_Dictionaries'"),
290 if (MsiViewExecute(view
, NULL
) == ERROR_SUCCESS
) {
291 while (ndicts
< MAX_LANGUAGES
&&
292 MsiViewFetch(view
, &record
) == ERROR_SUCCESS
)
294 length
= sizeof(feature
);
295 if (MsiRecordGetStringA(record
, 1, feature
, &length
)
299 feature
, "gm_r_ex_Dictionary_",
300 strlen("gm_r_ex_Dictionary_"))
305 feature
+ strlen("gm_r_ex_Dictionary_"));
306 dicts
[ndicts
].install
= false;
310 MsiCloseHandle(record
);
313 MsiCloseHandle(view
);
316 /* Keep track of what UI languages are relevant, either the ones explicitly
317 * requested with the UI_LANGS property, or all available on the system:
319 char* pVal
= nullptr;
320 if ( (GetMsiPropA( handle
, "UI_LANGS", &pVal
)) && pVal
) {
322 str_ptr
= strtok(pVal
, ",");
323 for(; str_ptr
!= nullptr ;) {
324 add_ui_lang(str_ptr
);
325 str_ptr
= strtok(nullptr, ",");
328 add_ui_lang(langid_to_string(GetSystemDefaultUILanguage()));
329 add_ui_lang(langid_to_string(LANGIDFROMLCID(GetThreadLocale())));
330 //TODO: are the above two explicit additions necessary, or will
331 // those values always be included in the below EnumUILanguages
333 if (GetMsiPropA(handle
, "ProductLanguage", &pVal
))
335 // This addition might refer to a language without an installed system language pack
336 // If the installer is run in this language, then this language is likely needed
337 long langid
= strtol(pVal
, nullptr, 10);
340 add_ui_lang(langid_to_string(static_cast<LANGID
>(langid
)));
342 EnumUILanguagesA(enum_ui_lang_proc
, 0, 0);
345 // If the set of langpacks that match any of the relevant UI languages is
346 // non-empty, select just those matching langpacks; otherwise, if an en_US
347 // langpack is included, select just that langpack (this happens if, e.g.,
348 // a multi-language en-US,de,es,fr,it,pt-BR installation set is installed on
349 // a Finnish Windows); otherwise, select all langpacks (this happens if,
350 // e.g., a single-language de installation set is installed on a Finnish
352 bool matches
= false;
353 for (int i
= 0; i
< nlangs
; i
++) {
354 if (present_in_ui_langs(langs
[i
].lang
)) {
355 langs
[i
].install
= true;
360 for (int i
= 0; i
< nlangs
; i
++) {
361 if (strcmp(langs
[i
].lang
, "en_US") == 0) {
362 langs
[i
].install
= true;
368 for (int i
= 0; i
< nlangs
; i
++) {
369 langs
[i
].install
= true;
374 for (int i
= 0; i
< nlangs
; i
++) {
375 if (langs
[i
].install
) {
376 addMatchingDictionaries(langs
[i
].lang
, dicts
, ndicts
);
378 sprintf(feature
, "gm_Langpack_r_%s", langs
[i
].lang
);
379 MsiSetFeatureStateA(handle
, feature
, INSTALLSTATE_ABSENT
);
383 // Select just those dictionaries that match any of the selected langpacks:
384 for (int i
= 0; i
!= ndicts
; ++i
) {
385 if (!dicts
[i
].install
) {
386 sprintf(feature
, "gm_r_ex_Dictionary_%s", dicts
[i
].lang
);
387 MsiSetFeatureStateA(handle
, feature
, INSTALLSTATE_ABSENT
);
391 MsiCloseHandle(database
);
393 return ERROR_SUCCESS
;
396 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */