Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 27 additions & 21 deletions source/languageHandler.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2007-2025 NV access Limited, Joseph Lee, Łukasz Golonka, Cyrille Bougot
# Copyright (C) 2007-2026 NV access Limited, Joseph Lee, Łukasz Golonka, Cyrille Bougot
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.

Expand All @@ -20,12 +20,6 @@
from logHandler import log
import winBindings.kernel32
import winKernel
from typing import (
FrozenSet,
List,
Optional,
Tuple,
)

# a few Windows locale constants
LOCALE_USER_DEFAULT = 0x400
Expand All @@ -41,16 +35,23 @@
#: or because it is not a legal locale name (e.g. "zzzz").
LCID_NONE = 0 # 0 used instead of None for backwards compatibility.

LANGS_WITHOUT_TRANSLATIONS: FrozenSet[str] = frozenset(("en",))
LANGS_WITHOUT_TRANSLATIONS: frozenset[str] = frozenset(("en",))

_language: str | None = None
"""Language of NVDA's UI.
"""

installedTranslation: Optional[weakref.ReferenceType] = None
installedTranslation: weakref.ReferenceType | None = None
"""Saved copy of the installed translation for ease of wrapping.
"""

_LCIDS_TO_TRANSLATED_LOCALES_OVERRIDES = {
# locale.windows_locale maps this to "kh_KH", which is incorrect.
# https://github.com/python/cpython/issues/123853
1107: "km_KH", # Khmer - Cambodia
}


LCIDS_TO_TRANSLATED_LOCALES = {
# Windows maps this to "ku-Arab-IQ", however a translation is added for
# Central Kurdish in localesData.LANG_NAMES_TO_LOCALIZED_DESCS["ckb"]
Expand Down Expand Up @@ -124,17 +125,22 @@ def localeNameToWindowsLCID(localeName: str) -> int:
return LCID


def windowsLCIDToLocaleName(lcid: int) -> Optional[str]:
def windowsLCIDToLocaleName(lcid: int) -> str | None:
"""
Gets a normalized locale from a Windows LCID.

NVDA should avoid relying on LCIDs in future, as they have been deprecated by MS:
https://docs.microsoft.com/en-us/globalization/locale/locale-names
"""
# From the locale.windows_locale in-line code documentation: (#4203)
# This list has been updated to include every locale up to Windows Vista.
# NOTE: this mapping is incomplete.
localeName = locale.windows_locale.get(lcid)

localeName = _LCIDS_TO_TRANSLATED_LOCALES_OVERRIDES.get(lcid)
if not localeName:
# From the locale.windows_locale in-line code documentation: (#4203)
# This list has been updated to include every locale up to Windows Vista.
# NOTE: this mapping is incomplete and out of date.
# We should stop relying on it and use Windows API calls instead.
# https://github.com/python/cpython/issues/123853
localeName = locale.windows_locale.get(lcid)
# Check a manual mapping before using Windows to look up the correct LCID locale name.
if not localeName:
localeName = LCIDS_TO_TRANSLATED_LOCALES.get(lcid)
Expand All @@ -144,7 +150,7 @@ def windowsLCIDToLocaleName(lcid: int) -> Optional[str]:
return normalizeLanguage(localeName)


def getLanguageDescription(language: str) -> Optional[str]:
def getLanguageDescription(language: str) -> weakref.ReferenceType | None:
"""Finds out the description (localized full name) of a given local name"""
if language == "Windows":
# Translators: the label for the Windows default NVDA interface language.
Expand Down Expand Up @@ -173,7 +179,7 @@ def getLanguageDescription(language: str) -> Optional[str]:
return desc


def englishLanguageNameFromNVDALocale(localeName: str) -> Optional[str]:
def englishLanguageNameFromNVDALocale(localeName: str) -> str | None:
"""Returns either English name of the given language using `GetLocaleInfoEx` or None
if the given locale is not known to Windows."""
localeName = normalizeLocaleForWin32(localeName)
Expand Down Expand Up @@ -206,7 +212,7 @@ def englishLanguageNameFromNVDALocale(localeName: str) -> Optional[str]:
return None


def englishCountryNameFromNVDALocale(localeName: str) -> Optional[str]:
def englishCountryNameFromNVDALocale(localeName: str) -> str | None:
"""Returns either English name of the given country using GetLocaleInfoEx or None
if the given locale is not known to Windows."""
localeName = normalizeLocaleForWin32(localeName)
Expand All @@ -225,7 +231,7 @@ def englishCountryNameFromNVDALocale(localeName: str) -> Optional[str]:
return None


def ansiCodePageFromNVDALocale(localeName: str) -> Optional[str]:
def ansiCodePageFromNVDALocale(localeName: str) -> str | None:
"""Returns either ANSI code page for a given locale using GetLocaleInfoEx or None
if the given locale is not known to Windows."""
localeName = normalizeLocaleForWin32(localeName)
Expand All @@ -250,7 +256,7 @@ def ansiCodePageFromNVDALocale(localeName: str) -> Optional[str]:
return None


def listNVDALocales() -> List[str]:
def listNVDALocales() -> list[str]:
# Make a list of all the locales found in NVDA's locale dir
localesDir = os.path.join(globalVars.appDir, "locale")
locales = [
Expand All @@ -268,7 +274,7 @@ def listNVDALocales() -> List[str]:
return locales


def getAvailableLanguages(presentational: bool = False) -> List[Tuple[str, str]]:
def getAvailableLanguages(presentational: bool = False) -> list[tuple[str, str]]:
"""generates a list of locale names, plus their full localized language and country names.
@param presentational: whether this is meant to be shown alphabetically by language description
"""
Expand Down Expand Up @@ -443,7 +449,7 @@ def getLanguage() -> str:
return _language


def normalizeLanguage(lang: str) -> Optional[str]:
def normalizeLanguage(lang: str) -> str | None:
"""
Normalizes a language-dialect string in to a standard form we can deal with.
Converts any dash to underline, and makes sure that language is lowercase and dialect is upercase.
Expand Down
2 changes: 2 additions & 0 deletions user_docs/en/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ The "Logging level" and "Allow NV Access to gather NVDA usage statistics" settin
The settings for Screen Curtain have also been moved here from the "Vision" category.
Additionally, Screen Curtain's settings are now configuration profile independent.

The NVDA interface is now translated to Cambodian.
Liblouis, Unicode CLDR and eSpeak NG have been updated.
Added tables for English Grade 3, Japanese (Rokuten Kanji), and Macedonian uncontracted braille.
Improved the Biblical Hebrew, Unified English Braille, Greek International, Hungarian, Norwegian, Portuguese 8-dot and Slovakian braille tables.
Expand Down Expand Up @@ -91,6 +92,7 @@ Such controls will now be accessible in browse mode where they weren't before. (
* The state of the Screen Curtain is no longer dependent on the configuration profile in use. (#10476)
* A new "Privacy and Security" category has been added to NVDA's settings dialog.
It currently includes Screen Curtain's settings (previously in the "Vision" category), and the "Logging level" and "Allow NV Access to gather NVDA usage statistics" settings (previously in the "General" category). (#19177, #19296)
* The NVDA interface is now translated to Cambodian. (#19450)

### Bug Fixes

Expand Down
4 changes: 2 additions & 2 deletions user_docs/en/userGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Major highlights include:
* Ability to [connect to and control another computer running NVDA](#RemoteAccess) for remote assistance or collaboration
* Ability to run entirely from a USB flash drive or other portable media without the need for installation
* Easy to use talking installer
* Translated into 55 languages
* Translated into 56 languages
* Support for [modern Windows Operating Systems](#MinimumSystemRequirements)
* Ability to run during Windows sign-in and [other secure screens](#SecureScreens)
* Announcing controls and text while using touch gestures
Expand Down Expand Up @@ -58,7 +58,7 @@ Windows Server 2016, 2019, 2022 and 2025.
### Internationalization {#Internationalization}

It is important that people anywhere in the world, no matter what language they speak, get equal access to technology.
Besides English, NVDA has been translated into 55 languages including: Afrikaans, Albanian, Amharic, Arabic, Aragonese, Bosnian, Bulgarian, Burmese, Catalan, Chinese (simplified and traditional), Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, Galician, Georgian, German (Germany and Switzerland), Greek, Hebrew, Hindi, Hungarian, Icelandic, Irish, Italian, Japanese, Kannada, Korean, Kyrgyz, Lithuanian, Macedonian, Mongolian, Nepali, Norwegian, Polish, Portuguese (Brazil and Portugal), Punjabi, Romanian, Russian, Serbian, Slovak, Slovenian, Spanish (Colombia and Spain), Swedish, Tamil, Thai, Turkish, Ukrainian and Vietnamese.
Besides English, NVDA has been translated into 56 languages including: Afrikaans, Albanian, Amharic, Arabic, Aragonese, Bosnian, Bulgarian, Burmese, Cambodian, Catalan, Chinese (simplified and traditional), Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, Galician, Georgian, German (Germany and Switzerland), Greek, Hebrew, Hindi, Hungarian, Icelandic, Irish, Italian, Japanese, Kannada, Korean, Kyrgyz, Lithuanian, Macedonian, Mongolian, Nepali, Norwegian, Polish, Portuguese (Brazil and Portugal), Punjabi, Romanian, Russian, Serbian, Slovak, Slovenian, Spanish (Colombia and Spain), Swedish, Tamil, Thai, Turkish, Ukrainian and Vietnamese.

### Speech Synthesizer Support {#SpeechSynthesizerSupport}

Expand Down
Loading