Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jupyter_server/i18n/__init__.py: 28%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

54 statements  

1"""Server functions for loading translations""" 

2 

3from __future__ import annotations 

4 

5import errno 

6import json 

7import re 

8from collections import defaultdict 

9from os.path import dirname 

10from os.path import join as pjoin 

11from typing import Any 

12 

13I18N_DIR = dirname(__file__) 

14# Cache structure: 

15# {'nbjs': { # Domain 

16# 'zh-CN': { # Language code 

17# <english string>: <translated string> 

18# ... 

19# } 

20# }} 

21TRANSLATIONS_CACHE: dict[str, Any] = {"nbjs": {}} 

22 

23 

24_accept_lang_re = re.compile( 

25 r""" 

26(?P<lang>[a-zA-Z]{1,8}(-[a-zA-Z]{1,8})?) 

27(\s*;\s*q\s*=\s* 

28 (?P<qvalue>[01](.\d+)?) 

29)?""", 

30 re.VERBOSE, 

31) 

32 

33 

34def parse_accept_lang_header(accept_lang): 

35 """Parses the 'Accept-Language' HTTP header. 

36 

37 Returns a list of language codes in *ascending* order of preference 

38 (with the most preferred language last). 

39 """ 

40 by_q = defaultdict(list) 

41 for part in accept_lang.split(","): 

42 m = _accept_lang_re.match(part.strip()) 

43 if not m: 

44 continue 

45 lang, qvalue = m.group("lang", "qvalue") 

46 # Browser header format is zh-CN, gettext uses zh_CN 

47 lang = lang.replace("-", "_") 

48 qvalue = 1.0 if qvalue is None else float(qvalue) 

49 if qvalue == 0: 

50 continue # 0 means not accepted 

51 by_q[qvalue].append(lang) 

52 

53 res = [] 

54 for _, langs in sorted(by_q.items()): 

55 res.extend(sorted(langs)) 

56 return res 

57 

58 

59def load(language, domain="nbjs"): 

60 """Load translations from an nbjs.json file""" 

61 try: 

62 f = open(pjoin(I18N_DIR, language, "LC_MESSAGES", "nbjs.json"), encoding="utf-8") # noqa: SIM115 

63 except OSError as e: 

64 if e.errno != errno.ENOENT: 

65 raise 

66 return {} 

67 

68 with f: 

69 data = json.load(f) 

70 return data["locale_data"][domain] 

71 

72 

73def cached_load(language, domain="nbjs"): 

74 """Load translations for one language, using in-memory cache if available""" 

75 domain_cache = TRANSLATIONS_CACHE[domain] 

76 try: 

77 return domain_cache[language] 

78 except KeyError: 

79 data = load(language, domain) 

80 domain_cache[language] = data 

81 return data 

82 

83 

84def combine_translations(accept_language, domain="nbjs"): 

85 """Combine translations for multiple accepted languages. 

86 

87 Returns data re-packaged in jed1.x format. 

88 """ 

89 lang_codes = parse_accept_lang_header(accept_language) 

90 combined: dict[str, Any] = {} 

91 for language in lang_codes: 

92 if language == "en": 

93 # en is default, all translations are in frontend. 

94 combined.clear() 

95 else: 

96 combined.update(cached_load(language, domain)) 

97 

98 combined[""] = {"domain": "nbjs"} 

99 

100 return {"domain": domain, "locale_data": {domain: combined}}