Coverage for app/venv/lib/python3.14/site-packages/weblate/machinery/apertium.py: 45%

45 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-07 07:15 +0000

1# Copyright © Michal Čihař <michal@weblate.org> 

2# 

3# SPDX-License-Identifier: GPL-3.0-or-later 

4from __future__ import annotations 

5 

6from itertools import chain 

7from typing import TYPE_CHECKING 

8 

9from django.core.exceptions import ValidationError 

10from django.utils.translation import gettext 

11 

12from .base import ( 

13 ResponseStatusMachineTranslation, 

14) 

15from .forms import URLMachineryForm 

16 

17if TYPE_CHECKING: 17 ↛ 18line 17 didn't jump to line 18 because the condition on line 17 was never true

18 from .base import ( 

19 DownloadTranslations, 

20 ) 

21 

22LANGUAGE_MAP = { 

23 "ca": "cat", 

24 "cy": "cym", 

25 "eo": "epo", 

26 "gl": "glg", 

27 "bs": "hbs_BS", 

28 "es": "spa", 

29 "en": "eng", 

30 "en_US": "eng", 

31 "en_UK": "eng", 

32 "nl": "nld", 

33 "de": "deu", 

34 "fr": "fra", 

35 "sl": "slv", 

36 "sr": "hbs", 

37 "nb_NO": "nob", 

38 "nn": "nno", 

39 "se": "sme", # codespell:ignore sme 

40 "oc": "oci", 

41 "pt": "por", 

42 "co": "cos", 

43 "fi": "fin", 

44 "ia": "ina", 

45 "ro": "ron", 

46 "cs": "ces", 

47 "sk": "slk", 

48 "ru": "rus", 

49 "av": "ava", 

50 "is": "isl", 

51 "pl": "pol", 

52 "kk": "kaz", 

53 "tt": "tat", 

54 "be": "bel", 

55 "uk": "ukr", 

56 "gn": "grn", 

57 "mt": "mlt", 

58 "it": "ita", 

59 "zh_Hant": "zho", 

60 "br": "bre", # codespell:ignore 

61 "qu": "qve", 

62 "an": "arg", 

63 "mr": "mar", 

64 "af": "afr", 

65 "fa": "pes", 

66 "el": "ell", 

67 "lv": "lvs", 

68 "as": "asm", 

69 "hi": "hin", 

70 "te": "tel", # codespell:ignore te 

71 "hy": "hye", 

72 "th": "tha", # codespell:ignore tha 

73 "mk": "mkd", 

74 "la": "lat", 

75 "ga": "gle", 

76 "sw": "swa", 

77 "hu": "hun", 

78 "ml": "mal", 

79} 

80 

81 

82class ApertiumAPYTranslation(ResponseStatusMachineTranslation): 

83 """Apertium machine translation support.""" 

84 

85 name = "Apertium APy" 

86 max_score = 88 

87 settings_form = URLMachineryForm 

88 request_timeout = 20 

89 

90 @property 

91 def all_langs(self): 

92 """Return all language codes known to service.""" 

93 return set(chain.from_iterable(self.supported_languages)) 

94 

95 def map_language_code(self, code): 

96 """Convert language to service specific code.""" 

97 code = super().map_language_code(code).replace("-", "_") 

98 # Force download of supported languages 

99 if code not in self.all_langs and code in LANGUAGE_MAP: 

100 return LANGUAGE_MAP[code] 

101 return code 

102 

103 def validate_settings(self) -> None: 

104 try: 

105 languages = self.download_languages() 

106 except Exception as error: 

107 raise ValidationError( 

108 gettext("Could not fetch supported languages: %s") % error 

109 ) from error 

110 try: 

111 source_language, target_language = languages[0] 

112 except IndexError as error: 

113 raise ValidationError( 

114 gettext("No supported languages found: %s") % error 

115 ) from error 

116 try: 

117 self.download_multiple_translations( 

118 source_language, target_language, [("test", None)], None, 75 

119 ) 

120 except Exception as error: 

121 raise ValidationError( 

122 gettext("Could not fetch translation: %s") % error 

123 ) from error 

124 

125 def download_languages(self): 

126 """Download list of supported languages from a service.""" 

127 data = self.request("get", self.get_api_url("listPairs")).json() 

128 return [ 

129 (item["sourceLanguage"], item["targetLanguage"]) 

130 for item in data["responseData"] 

131 ] 

132 

133 def is_supported(self, source_language, target_language): 

134 """Check whether given language combination is supported.""" 

135 return (source_language, target_language) in self.supported_languages 

136 

137 def download_translations( 

138 self, 

139 source_language, 

140 target_language, 

141 text: str, 

142 unit, 

143 user, 

144 threshold: int = 75, 

145 ) -> DownloadTranslations: 

146 """Download list of possible translations from Apertium.""" 

147 args = { 

148 "langpair": f"{source_language}|{target_language}", 

149 "q": text, 

150 "markUnknown": "no", 

151 } 

152 response = self.request( 

153 "get", self.get_api_url("translate"), params=args 

154 ).json() 

155 

156 yield { 

157 "text": response["responseData"]["translatedText"], 

158 "quality": self.max_score, 

159 "service": self.name, 

160 "source": text, 

161 }