11# A part of NonVisual Desktop Access (NVDA)
2- # Copyright (C) 2007-2021 NV access Limited, Joseph Lee
2+ # Copyright (C) 2007-2021 NV access Limited, Joseph Lee, Łukasz Golonka
33# This file is covered by the GNU General Public License.
44# See the file COPYING for more details.
55
1818import globalVars
1919from logHandler import log
2020import winKernel
21- from typing import Optional
21+ from typing import List , Optional , Tuple
2222
2323#a few Windows locale constants
2424LOCALE_USER_DEFAULT = 0x400
3434#: or because it is not a legal locale name (e.g. "zzzz").
3535LCID_NONE = 0 # 0 used instead of None for backwards compatibility.
3636
37- curLang = "en"
38-
3937
4038class LOCALE (enum .IntEnum ):
4139 # Represents NLS constants which can be used with `GetLocaleInfoEx` or `GetLocaleInfoW`
@@ -118,8 +116,12 @@ def windowsLCIDToLocaleName(lcid: int) -> Optional[str]:
118116 if lang :
119117 return normalizeLanguage (lang )
120118
121- def getLanguageDescription (language ):
119+
120+ def getLanguageDescription (language : str ) -> Optional [str ]:
122121 """Finds out the description (localized full name) of a given local name"""
122+ if language == "Windows" :
123+ # Translators: the label for the Windows default NVDA interface language.
124+ return _ ("User default" )
123125 desc = None
124126 LCID = localeNameToWindowsLCID (language )
125127 if LCID is not LCID_NONE :
@@ -218,21 +220,27 @@ def ansiCodePageFromNVDALocale(localeName: str) -> Optional[str]:
218220 return None
219221
220222
221- def getAvailableLanguages (presentational = False ):
222- """generates a list of locale names, plus their full localized language and country names.
223- @param presentational: whether this is meant to be shown alphabetically by language description
224- @type presentational: bool
225- @rtype: list of tuples
226- """
227- #Make a list of all the locales found in NVDA's locale dir
223+ def listNVDALocales () -> List [str ]:
224+ # Make a list of all the locales found in NVDA's locale dir
228225 localesDir = os .path .join (globalVars .appDir , 'locale' )
229226 locales = [
230227 x for x in os .listdir (localesDir ) if os .path .isfile (os .path .join (localesDir , x , 'LC_MESSAGES' , 'nvda.mo' ))
231228 ]
232- #Make sure that en (english) is in the list as it may not have any locale files, but is default
229+ # Make sure that en (english) is in the list as it may not have any locale files, but is default
233230 if 'en' not in locales :
234231 locales .append ('en' )
235232 locales .sort ()
233+ # include a 'user default, windows' language,
234+ # which just represents the default language for this user account
235+ locales .insert (0 , "Windows" )
236+ return locales
237+
238+
239+ def getAvailableLanguages (presentational : bool = False ) -> List [Tuple [str , str ]]:
240+ """generates a list of locale names, plus their full localized language and country names.
241+ @param presentational: whether this is meant to be shown alphabetically by language description
242+ """
243+ locales = listNVDALocales ()
236244 # Prepare a 2-tuple list of language code and human readable language description.
237245 langs = [(lc , getLanguageDescription (lc )) for lc in locales ]
238246 # Translators: The pattern defining how languages are displayed and sorted in in the general
@@ -243,13 +251,14 @@ def getAvailableLanguages(presentational=False):
243251 isDescFirst = fullDescPattern .find ("{desc}" ) < fullDescPattern .find ("{lc}" )
244252 if presentational and isDescFirst :
245253 langs .sort (key = lambda lang : locale .strxfrm (lang [1 ] if lang [1 ] else lang [0 ]))
246- langs = [(lc , (fullDescPattern .format (desc = desc , lc = lc ) if desc else lc )) for lc , desc in langs ]
247- #include a 'user default, windows' language, which just represents the default language for this user account
248- langs .insert (
249- 0 ,
250- # Translators: the label for the Windows default NVDA interface language.
251- ("Windows" , _ ("User default" ))
252- )
254+ # Make sure that the 'user default' language is first in the list.
255+ for index , lang in enumerate (langs ):
256+ if lang [0 ] == "Windows" :
257+ break
258+ userDefault = langs .pop (index )
259+ langs = [userDefault ] + [
260+ (lc , (fullDescPattern .format (desc = desc , lc = lc ) if desc else lc )) for lc , desc in langs
261+ ]
253262 return langs
254263
255264
@@ -274,6 +283,12 @@ def pgettext(context, message):
274283 raise ValueError ("%s is Not a GNUTranslations or NullTranslations object" % translations )
275284 return pgettext
276285
286+
287+ def isLanguageForced () -> bool :
288+ """Returns `True` if language is provided from the command line - `False` otherwise."""
289+ return any (filter (lambda elem : elem .startswith ("--lang=" ), sys .argv ))
290+
291+
277292def getWindowsLanguage ():
278293 """
279294 Fetches the locale name of the user's configured language in Windows.
@@ -304,10 +319,9 @@ def setLanguage(lang: str) -> None:
304319 Sets the following using `lang` such as "en", "ru_RU", or "es-ES". Use "Windows" to use the system locale
305320 - the windows locale for the thread (fallback to system locale)
306321 - the translation service (fallback to English)
307- - languageHandler.curLang (match the translation service)
322+ - globalVars.appArgs.language (match the translation service)
308323 - the python locale for the thread (match the translation service, fallback to system default)
309324 '''
310- global curLang
311325 if lang == "Windows" :
312326 localeName = getWindowsLanguage ()
313327 else :
@@ -321,20 +335,20 @@ def setLanguage(lang: str) -> None:
321335
322336 try :
323337 trans = gettext .translation ("nvda" , localedir = "locale" , languages = [localeName ])
324- curLang = localeName
338+ globalVars . appArgs . language = localeName
325339 except IOError :
326340 try :
327341 log .debugWarning (f"couldn't set the translation service locale to { localeName } " )
328342 localeName = localeName .split ("_" )[0 ]
329343 trans = gettext .translation ("nvda" , localedir = "locale" , languages = [localeName ])
330- curLang = localeName
344+ globalVars . appArgs . language = localeName
331345 except IOError :
332346 log .debugWarning (f"couldn't set the translation service locale to { localeName } " )
333347 trans = gettext .translation ("nvda" , fallback = True )
334- curLang = "en"
348+ globalVars . appArgs . language = "en"
335349
336350 trans .install ()
337- setLocale (curLang )
351+ setLocale (getLanguage () )
338352 # Install our pgettext function.
339353 builtins .pgettext = makePgettext (trans )
340354
@@ -375,7 +389,8 @@ def _setPythonLocale(localeString: str) -> bool:
375389def setLocale (localeName : str ) -> None :
376390 '''
377391 Set python's locale using a `localeName` such as "en", "ru_RU", or "es-ES".
378- Will fallback on `curLang` if it cannot be set and finally fallback to the system locale.
392+ Will fallback on `globalVars.appArgs.language` if it cannot be set
393+ and finally fallback to the system locale.
379394 Passing NVDA locales straight to python `locale.setlocale` does now work since it tries to normalize the
380395 parameter using `locale.normalize` which results in locales unknown to Windows (Python issue 37945).
381396 For example executing: `locale.setlocale(locale.LC_ALL, "pl")`
@@ -409,21 +424,21 @@ def setLocale(localeName: str) -> None:
409424 return
410425 # Either Windows does not know the locale, or Python is unable to handle it.
411426 # reset to default locale
412- if originalLocaleName == curLang :
427+ if originalLocaleName == getLanguage () :
413428 # reset to system locale default if we can't set the current lang's locale
414429 locale .setlocale (locale .LC_ALL , "" )
415430 log .debugWarning (f"set python locale to system default" )
416431 else :
417- log .debugWarning (f"setting python locale to the current language { curLang } " )
432+ log .debugWarning (f"setting python locale to the current language { getLanguage () } " )
418433 # fallback and try to reset the locale to the current lang
419- setLocale (curLang )
434+ setLocale (getLanguage () )
420435
421436
422437def getLanguage () -> str :
423- return curLang
438+ return globalVars . appArgs . language
424439
425440
426- def normalizeLanguage (lang ) -> Optional [str ]:
441+ def normalizeLanguage (lang : str ) -> Optional [str ]:
427442 """
428443 Normalizes a language-dialect string in to a standard form we can deal with.
429444 Converts any dash to underline, and makes sure that language is lowercase and dialect is upercase.
0 commit comments