Skip to content

Commit cac303a

Browse files
authored
Fix handling language packs for pseudo language (#456)
* Fixes to support pseudo-language The pseudo-language allows for in-app translation of the interface. * Fix bugs to ensure the selected language menu is consistent
1 parent 317f27e commit cac303a

File tree

2 files changed

+46
-10
lines changed

2 files changed

+46
-10
lines changed

jupyterlab_server/settings_utils.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,13 @@
1717
from jupyter_server.services.config.manager import ConfigManager, recursive_update
1818
from tornado import web
1919

20-
from .translation_utils import DEFAULT_LOCALE, L10N_SCHEMA_NAME, SYS_LOCALE, is_valid_locale
20+
from .translation_utils import (
21+
DEFAULT_LOCALE,
22+
L10N_SCHEMA_NAME,
23+
PSEUDO_LANGUAGE,
24+
SYS_LOCALE,
25+
is_valid_locale,
26+
)
2127

2228
# The JupyterLab settings file extension.
2329
SETTINGS_EXTENSION = ".jupyterlab-settings"
@@ -497,7 +503,7 @@ def get_current_locale(self) -> str:
497503
current_locale = settings.get("settings", {}).get("locale") or SYS_LOCALE
498504
if current_locale == "default":
499505
current_locale = SYS_LOCALE
500-
if not is_valid_locale(current_locale):
506+
if not is_valid_locale(current_locale) and current_locale != PSEUDO_LANGUAGE:
501507
current_locale = DEFAULT_LOCALE
502508

503509
return current_locale

jupyterlab_server/translation_utils.py

+38-8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
Localization utilities to find available language packs and packages with
66
localization data.
77
"""
8+
89
from __future__ import annotations
910

1011
import gettext
@@ -40,6 +41,9 @@
4041
L10N_SCHEMA_NAME = "@jupyterlab/translation-extension:plugin"
4142
PY37_OR_LOWER = sys.version_info[:2] <= (3, 7)
4243

44+
# Pseudo language locale for in-context translation
45+
PSEUDO_LANGUAGE = "ach_UG"
46+
4347
_default_schema_context = "schema"
4448
_default_settings_context = "settings"
4549
_lab_i18n_config = "jupyter.lab.internationalization"
@@ -151,7 +155,9 @@ def is_valid_locale(locale_: str) -> bool:
151155
- Brazilian German: "de_BR"
152156
"""
153157
# Add exception for Norwegian
154-
if locale_ == "no_NO":
158+
if locale_ in {
159+
"no_NO",
160+
}:
155161
return True
156162

157163
valid = False
@@ -183,8 +189,11 @@ def get_display_name(locale_: str, display_locale: str = DEFAULT_LOCALE) -> str:
183189
"""
184190
locale_ = locale_ if is_valid_locale(locale_) else DEFAULT_LOCALE
185191
display_locale = display_locale if is_valid_locale(display_locale) else DEFAULT_LOCALE
186-
loc = babel.Locale.parse(locale_)
187-
display_name = loc.get_display_name(display_locale)
192+
try:
193+
loc = babel.Locale.parse(locale_)
194+
display_name = loc.get_display_name(display_locale)
195+
except babel.UnknownLocaleError:
196+
display_name = display_locale
188197
if display_name:
189198
display_name = display_name[0].upper() + display_name[1:]
190199
return display_name # type:ignore[return-value]
@@ -312,21 +321,38 @@ def get_language_packs(display_locale: str = DEFAULT_LOCALE) -> tuple[dict, str]
312321
else:
313322
invalid_locales.append(locale_)
314323

315-
display_locale = display_locale if display_locale in valid_locales else DEFAULT_LOCALE
324+
display_locale_ = display_locale if display_locale in valid_locales else DEFAULT_LOCALE
316325
locales = {
317326
DEFAULT_LOCALE: {
318-
"displayName": get_display_name(DEFAULT_LOCALE, display_locale),
327+
"displayName": (
328+
get_display_name(DEFAULT_LOCALE, display_locale_)
329+
if display_locale != PSEUDO_LANGUAGE
330+
else "Default"
331+
),
319332
"nativeName": get_display_name(DEFAULT_LOCALE, DEFAULT_LOCALE),
320333
}
321334
}
322335
for locale_ in valid_locales:
323336
locales[locale_] = {
324-
"displayName": get_display_name(locale_, display_locale),
337+
"displayName": get_display_name(locale_, display_locale_),
325338
"nativeName": get_display_name(locale_, locale_),
326339
}
327340

328341
if invalid_locales:
329-
messages.append(f"The following locales are invalid: {invalid_locales}!")
342+
if PSEUDO_LANGUAGE in invalid_locales:
343+
invalid_locales.remove(PSEUDO_LANGUAGE)
344+
locales[PSEUDO_LANGUAGE] = {
345+
"displayName": "Pseudo-language",
346+
# Trick to ensure the proper language is selected in the language menu
347+
"nativeName": (
348+
"to translate the UI"
349+
if display_locale != PSEUDO_LANGUAGE
350+
else "Pseudo-language"
351+
),
352+
}
353+
# Check again as the pseudo-language was maybe the only invalid locale
354+
if invalid_locales:
355+
messages.append(f"The following locales are invalid: {invalid_locales}!")
330356

331357
return locales, "\n".join(messages)
332358

@@ -351,7 +377,11 @@ def get_language_pack(locale_: str) -> tuple:
351377
found_packages_locales, message = get_installed_packages_locale(locale_)
352378
locale_data = {}
353379
messages = message.split("\n")
354-
if not message and is_valid_locale(locale_) and locale_ in found_locales:
380+
if (
381+
not message
382+
and (locale_ == PSEUDO_LANGUAGE or is_valid_locale(locale_))
383+
and locale_ in found_locales
384+
):
355385
path = found_locales[locale_]
356386
for root, __, files in os.walk(path, topdown=False):
357387
for name in files:

0 commit comments

Comments
 (0)