@danhachuel/thunderbolt 0.3.28 → 0.3.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/hermes_ui/api_key_tests.py +266 -0
- package/hermes_ui/languages.py +88 -0
- package/package.json +1 -1
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""Safe, read-only API credential diagnostics used by the Settings UI.
|
|
2
|
+
|
|
3
|
+
Every check in this module is deliberately bounded and avoids uploads, posts,
|
|
4
|
+
actor runs, image generation and audio generation. Results contain no secret,
|
|
5
|
+
URL or raw exception text; callers may persist them in local settings safely.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
from datetime import datetime, timezone
|
|
12
|
+
from typing import Any
|
|
13
|
+
from urllib.parse import quote, urlsplit, urlunsplit
|
|
14
|
+
|
|
15
|
+
import requests
|
|
16
|
+
|
|
17
|
+
from app.modules.niche_finder.apify import APIFY_API_BASE
|
|
18
|
+
|
|
19
|
+
DEFAULT_TIMEOUT = 20
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _result(status: str, message: str, *, status_code: int | None = None) -> dict[str, Any]:
|
|
23
|
+
"""Build a small persistence-safe result object."""
|
|
24
|
+
return {
|
|
25
|
+
"ok": status == "success",
|
|
26
|
+
"status": status,
|
|
27
|
+
"message": message,
|
|
28
|
+
"status_code": status_code,
|
|
29
|
+
"checked_at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _missing(message: str = "Introduza a credencial antes de testar.") -> dict[str, Any]:
|
|
34
|
+
return _result("missing", message)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _unsupported(message: str) -> dict[str, Any]:
|
|
38
|
+
return _result("unsupported", message)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _safe_url(value: str) -> str:
|
|
42
|
+
"""Keep a configured base URL's scheme/host only; never echo credentials."""
|
|
43
|
+
try:
|
|
44
|
+
parsed = urlsplit(str(value or "").strip())
|
|
45
|
+
if parsed.scheme not in {"http", "https"} or not parsed.netloc:
|
|
46
|
+
return ""
|
|
47
|
+
return urlunsplit((parsed.scheme, parsed.netloc, parsed.path.rstrip("/"), "", ""))
|
|
48
|
+
except ValueError:
|
|
49
|
+
return ""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _response_result(response: Any) -> dict[str, Any]:
|
|
53
|
+
status_code = int(getattr(response, "status_code", 0) or 0)
|
|
54
|
+
if 200 <= status_code < 300:
|
|
55
|
+
return _result("success", "API Key OK", status_code=status_code)
|
|
56
|
+
if status_code in {401, 403}:
|
|
57
|
+
return _result("error", "A API rejeitou a credencial.", status_code=status_code)
|
|
58
|
+
if status_code == 404:
|
|
59
|
+
return _result("error", "O endpoint de diagnóstico não está disponível.", status_code=status_code)
|
|
60
|
+
if status_code == 429:
|
|
61
|
+
return _result("error", "A API limitou a chamada de diagnóstico.", status_code=status_code)
|
|
62
|
+
return _result("error", "A chamada de diagnóstico falhou.", status_code=status_code or None)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _get(url: str, **kwargs: Any) -> dict[str, Any]:
|
|
66
|
+
try:
|
|
67
|
+
response = requests.get(url, timeout=DEFAULT_TIMEOUT, **kwargs)
|
|
68
|
+
except requests.RequestException:
|
|
69
|
+
return _result("error", "Não foi possível contactar o serviço.")
|
|
70
|
+
return _response_result(response)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _post(url: str, **kwargs: Any) -> dict[str, Any]:
|
|
74
|
+
try:
|
|
75
|
+
response = requests.post(url, timeout=DEFAULT_TIMEOUT, **kwargs)
|
|
76
|
+
except requests.RequestException:
|
|
77
|
+
return _result("error", "Não foi possível contactar o serviço.")
|
|
78
|
+
return _response_result(response)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_kaggle_credentials(username: str, api_key: str) -> dict[str, Any]:
|
|
82
|
+
"""Validate Kaggle Basic Auth with a read-only user lookup."""
|
|
83
|
+
username = str(username or "").strip()
|
|
84
|
+
api_key = str(api_key or "").strip()
|
|
85
|
+
if not username or not api_key:
|
|
86
|
+
return _missing("Introduza o username e a API key Kaggle antes de testar.")
|
|
87
|
+
return _get(f"https://www.kaggle.com/api/v1/users/list/{quote(username, safe='')}", auth=(username, api_key))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_apify_credentials(api_token: str) -> dict[str, Any]:
|
|
91
|
+
"""Validate an Apify token without starting an Actor or reading a dataset."""
|
|
92
|
+
api_token = str(api_token or "").strip()
|
|
93
|
+
if not api_token:
|
|
94
|
+
return _missing()
|
|
95
|
+
return _get(f"{APIFY_API_BASE}/users/me", headers={"Authorization": f"Bearer {api_token}"})
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_nano_banana_credentials(api_key: str, model: str) -> dict[str, Any]:
|
|
99
|
+
"""Validate the configured Gemini image model metadata without generating an image."""
|
|
100
|
+
api_key = str(api_key or "").strip()
|
|
101
|
+
model = str(model or "").strip()
|
|
102
|
+
if not api_key:
|
|
103
|
+
return _missing()
|
|
104
|
+
if not model:
|
|
105
|
+
return _result("missing", "Complete a configuração do modelo antes de testar.")
|
|
106
|
+
model_path = model if model.startswith("models/") else f"models/{model}"
|
|
107
|
+
return _get(
|
|
108
|
+
f"https://generativelanguage.googleapis.com/v1beta/{model_path}",
|
|
109
|
+
headers={"x-goog-api-key": api_key},
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def test_azure_speech_credentials(api_key: str, region: str) -> dict[str, Any]:
|
|
114
|
+
"""Validate Azure Speech by listing voices; this does not synthesize audio."""
|
|
115
|
+
api_key = str(api_key or "").strip()
|
|
116
|
+
region = str(region or "").strip().lower()
|
|
117
|
+
if not api_key or not region:
|
|
118
|
+
return _missing("Introduza a Azure Speech key e a região antes de testar.")
|
|
119
|
+
if not region.replace("-", "").isalnum():
|
|
120
|
+
return _result("error", "A região Azure Speech não é válida.")
|
|
121
|
+
endpoint = f"https://{region}.tts.speech.microsoft.com/tts/cognitiveservices/voices/list"
|
|
122
|
+
return _get(endpoint, headers={"Ocp-Apim-Subscription-Key": api_key})
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def test_elevenlabs_credentials(api_key: str) -> dict[str, Any]:
|
|
126
|
+
"""Validate ElevenLabs with the documented read-only models endpoint."""
|
|
127
|
+
api_key = str(api_key or "").strip()
|
|
128
|
+
if not api_key:
|
|
129
|
+
return _missing()
|
|
130
|
+
return _get("https://api.elevenlabs.io/v1/models", headers={"xi-api-key": api_key})
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _models_endpoint(base_url: str) -> str:
|
|
134
|
+
base = _safe_url(base_url)
|
|
135
|
+
if not base:
|
|
136
|
+
return ""
|
|
137
|
+
return f"{base}/models" if base.endswith("/v1") else f"{base}/v1/models"
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_siliconflow_credentials(api_key: str) -> dict[str, Any]:
|
|
141
|
+
"""Validate SiliconFlow with its read-only model catalogue."""
|
|
142
|
+
api_key = str(api_key or "").strip()
|
|
143
|
+
if not api_key:
|
|
144
|
+
return _missing()
|
|
145
|
+
return _get("https://api.siliconflow.cn/v1/models", headers={"Authorization": f"Bearer {api_key}"})
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def test_minimax_credentials(api_key: str, base_url: str) -> dict[str, Any]:
|
|
149
|
+
"""Validate a configured MiniMax endpoint without calling text-to-audio."""
|
|
150
|
+
api_key = str(api_key or "").strip()
|
|
151
|
+
endpoint = _models_endpoint(base_url)
|
|
152
|
+
if not api_key or not str(base_url or "").strip():
|
|
153
|
+
return _missing("Introduza a MiniMax TTS key e a Base URL antes de testar.")
|
|
154
|
+
if not endpoint:
|
|
155
|
+
return _result("error", "A Base URL MiniMax não é válida.")
|
|
156
|
+
return _get(endpoint, headers={"Authorization": f"Bearer {api_key}"})
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def test_openai_compatible_voice_credentials(provider: str, api_key: str, base_url: str) -> dict[str, Any]:
|
|
160
|
+
"""Validate a local/custom voice service through a read-only models endpoint."""
|
|
161
|
+
provider = str(provider or "serviço").strip()
|
|
162
|
+
api_key = str(api_key or "").strip()
|
|
163
|
+
endpoint = _models_endpoint(base_url)
|
|
164
|
+
if not str(base_url or "").strip():
|
|
165
|
+
return _missing(f"Introduza a Base URL de {provider} antes de testar.")
|
|
166
|
+
if not endpoint:
|
|
167
|
+
return _result("error", f"A Base URL de {provider} não é válida.")
|
|
168
|
+
headers = {"Authorization": f"Bearer {api_key}"} if api_key else {}
|
|
169
|
+
return _get(endpoint, headers=headers)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def test_suno_credentials(api_key: str, base_url: str, endpoint: str) -> dict[str, Any]:
|
|
173
|
+
"""Avoid a Suno generation call: custom deployments have no common safe health API."""
|
|
174
|
+
if not str(api_key or "").strip() or not str(base_url or "").strip():
|
|
175
|
+
return _missing("Introduza a Suno API key e a Base URL antes de testar.")
|
|
176
|
+
return _unsupported("Suno requer um endpoint de diagnóstico fornecido pelo serviço; o Thunderbolt não inicia uma geração só para testar.")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def test_tiktok_credentials(client_key: str, client_secret: str, access_token: str = "") -> dict[str, Any]:
|
|
180
|
+
"""Validate TikTok only when an OAuth access token is available."""
|
|
181
|
+
client_key = str(client_key or "").strip()
|
|
182
|
+
client_secret = str(client_secret or "").strip()
|
|
183
|
+
token = str(access_token or os.getenv("TIKTOK_ACCESS_TOKEN", "") or "").strip()
|
|
184
|
+
if not client_key or not client_secret:
|
|
185
|
+
return _missing("Introduza o Client ID e o Client Secret TikTok antes de testar.")
|
|
186
|
+
if not token:
|
|
187
|
+
return _unsupported("TikTok só permite uma chamada autenticada depois da autorização OAuth; conclua o Playground e guarde um access token.")
|
|
188
|
+
return _get(
|
|
189
|
+
"https://open.tiktokapis.com/v2/user/info/?fields=open_id",
|
|
190
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def test_upload_post_credentials(api_key: str, base_url: str = "https://api.upload-post.com/api") -> dict[str, Any]:
|
|
195
|
+
"""Validate Upload-Post through GET /uploadposts/me, never through an upload."""
|
|
196
|
+
api_key = str(api_key or "").strip()
|
|
197
|
+
base = _safe_url(base_url or "https://api.upload-post.com/api")
|
|
198
|
+
if not api_key:
|
|
199
|
+
return _missing()
|
|
200
|
+
if not base:
|
|
201
|
+
return _result("error", "A Base URL Upload-Post não é válida.")
|
|
202
|
+
return _get(f"{base}/uploadposts/me", headers={"Authorization": f"Apikey {api_key}"})
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def test_postiz_credentials(api_key: str, base_url: str = "https://api.postiz.com/public/v1") -> dict[str, Any]:
|
|
206
|
+
"""Validate Postiz with the read-only integrations endpoint."""
|
|
207
|
+
api_key = str(api_key or "").strip()
|
|
208
|
+
base = _safe_url(base_url or "https://api.postiz.com/public/v1")
|
|
209
|
+
if not api_key:
|
|
210
|
+
return _missing()
|
|
211
|
+
if not base:
|
|
212
|
+
return _result("error", "A Base URL Postiz não é válida.")
|
|
213
|
+
return _get(f"{base}/integrations", headers={"Authorization": api_key})
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def test_material_source_credentials(provider: str, api_key: str) -> dict[str, Any]:
|
|
217
|
+
"""Run a read-only provider-specific check for material source cards."""
|
|
218
|
+
provider = str(provider or "").strip().lower()
|
|
219
|
+
api_key = str(api_key or "").strip()
|
|
220
|
+
if not api_key:
|
|
221
|
+
return _missing()
|
|
222
|
+
if provider == "pexels":
|
|
223
|
+
return _get("https://api.pexels.com/v1/curated", params={"per_page": 1}, headers={"Authorization": api_key})
|
|
224
|
+
if provider == "pixabay":
|
|
225
|
+
return _get("https://pixabay.com/api/", params={"key": api_key, "per_page": 3})
|
|
226
|
+
if provider == "coverr":
|
|
227
|
+
return _get("https://api.coverr.co/v1/videos", headers={"Authorization": f"Bearer {api_key}"})
|
|
228
|
+
return _unsupported("Este provider de materiais não expõe um endpoint de diagnóstico seguro no cartão actual.")
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def test_voice_provider(provider: str, settings: dict[str, Any]) -> dict[str, Any]:
|
|
232
|
+
"""Dispatch a non-generating check for one provider in the voice/music group."""
|
|
233
|
+
provider = str(provider or "").strip().lower()
|
|
234
|
+
if provider == "azure_speech":
|
|
235
|
+
return test_azure_speech_credentials(settings.get("azure_speech_key", ""), settings.get("azure_speech_region", ""))
|
|
236
|
+
if provider == "elevenlabs":
|
|
237
|
+
return test_elevenlabs_credentials(settings.get("elevenlabs_api_key", ""))
|
|
238
|
+
if provider == "siliconflow":
|
|
239
|
+
return test_siliconflow_credentials(settings.get("siliconflow_tts_api_key", ""))
|
|
240
|
+
if provider == "minimax":
|
|
241
|
+
return test_minimax_credentials(settings.get("minimax_tts_api_key", ""), settings.get("minimax_tts_base_url", ""))
|
|
242
|
+
if provider == "chatterbox":
|
|
243
|
+
return test_openai_compatible_voice_credentials("Chatterbox", settings.get("chatterbox_api_key", ""), settings.get("chatterbox_base_url", ""))
|
|
244
|
+
if provider == "sonilo":
|
|
245
|
+
return test_openai_compatible_voice_credentials("Sonilo", settings.get("sonilo_api_key", ""), settings.get("sonilo_base_url", ""))
|
|
246
|
+
if provider == "suno":
|
|
247
|
+
return test_suno_credentials(settings.get("suno_api_key", ""), settings.get("suno_api_base_url", ""), settings.get("suno_api_endpoint", ""))
|
|
248
|
+
return _unsupported("Este provider de voz não tem diagnóstico remoto configurado.")
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
__all__ = [
|
|
252
|
+
"test_apify_credentials",
|
|
253
|
+
"test_azure_speech_credentials",
|
|
254
|
+
"test_elevenlabs_credentials",
|
|
255
|
+
"test_kaggle_credentials",
|
|
256
|
+
"test_material_source_credentials",
|
|
257
|
+
"test_minimax_credentials",
|
|
258
|
+
"test_nano_banana_credentials",
|
|
259
|
+
"test_openai_compatible_voice_credentials",
|
|
260
|
+
"test_postiz_credentials",
|
|
261
|
+
"test_siliconflow_credentials",
|
|
262
|
+
"test_suno_credentials",
|
|
263
|
+
"test_tiktok_credentials",
|
|
264
|
+
"test_upload_post_credentials",
|
|
265
|
+
"test_voice_provider",
|
|
266
|
+
]
|
package/hermes_ui/languages.py
CHANGED
|
@@ -1222,3 +1222,91 @@ _MATERIAL_SOURCE_TRANSLATIONS: dict[str, dict[str, str]] = {
|
|
|
1222
1222
|
}
|
|
1223
1223
|
for _language_code, _material_source_translation in _MATERIAL_SOURCE_TRANSLATIONS.items():
|
|
1224
1224
|
UI_TRANSLATIONS[_language_code].update(_material_source_translation)
|
|
1225
|
+
|
|
1226
|
+
|
|
1227
|
+
# API credential diagnostics. Keep these keys stable because the settings
|
|
1228
|
+
# renderer calls ui_text() for every visible result in all supported languages.
|
|
1229
|
+
_API_TEST_TRANSLATIONS: dict[str, dict[str, str]] = {
|
|
1230
|
+
"pt": {
|
|
1231
|
+
"A testar chamada API…": "A testar chamada API…",
|
|
1232
|
+
"Último teste: API Key OK": "Último teste: API Key OK",
|
|
1233
|
+
"Último teste: falta configuração": "Último teste: falta configuração",
|
|
1234
|
+
"Último teste: requer autorização ou endpoint seguro": "Último teste: requer autorização ou endpoint seguro",
|
|
1235
|
+
"Último teste: chamada falhou": "Último teste: chamada falhou",
|
|
1236
|
+
"Testar credenciais TTS e música": "Testar credenciais TTS e música",
|
|
1237
|
+
},
|
|
1238
|
+
"en": {
|
|
1239
|
+
"A testar chamada API…": "Testing API call…",
|
|
1240
|
+
"Último teste: API Key OK": "Last test: API key OK",
|
|
1241
|
+
"Último teste: falta configuração": "Last test: configuration missing",
|
|
1242
|
+
"Último teste: requer autorização ou endpoint seguro": "Last test: additional authorization or a safe endpoint is required",
|
|
1243
|
+
"Último teste: chamada falhou": "Last test: call failed",
|
|
1244
|
+
"Testar credenciais TTS e música": "Test TTS and music credentials",
|
|
1245
|
+
},
|
|
1246
|
+
"zh": {
|
|
1247
|
+
"A testar chamada API…": "正在测试 API 调用…",
|
|
1248
|
+
"Último teste: API Key OK": "上次测试:API 密钥正常",
|
|
1249
|
+
"Último teste: falta configuração": "上次测试:缺少配置",
|
|
1250
|
+
"Último teste: requer autorização ou endpoint seguro": "上次测试:需要额外授权或安全端点",
|
|
1251
|
+
"Último teste: chamada falhou": "上次测试:调用失败",
|
|
1252
|
+
"Testar credenciais TTS e música": "测试 TTS 和音乐凭证",
|
|
1253
|
+
},
|
|
1254
|
+
"de": {
|
|
1255
|
+
"A testar chamada API…": "API-Aufruf wird getestet…",
|
|
1256
|
+
"Último teste: API Key OK": "Letzter Test: API-Schlüssel OK",
|
|
1257
|
+
"Último teste: falta configuração": "Letzter Test: Konfiguration fehlt",
|
|
1258
|
+
"Último teste: requer autorização ou endpoint seguro": "Letzter Test: zusätzliche Autorisierung oder ein sicherer Endpunkt erforderlich",
|
|
1259
|
+
"Último teste: chamada falhou": "Letzter Test: Aufruf fehlgeschlagen",
|
|
1260
|
+
"Testar credenciais TTS e música": "TTS- und Musik-Anmeldedaten testen",
|
|
1261
|
+
},
|
|
1262
|
+
"vi": {
|
|
1263
|
+
"A testar chamada API…": "Đang kiểm tra lệnh gọi API…",
|
|
1264
|
+
"Último teste: API Key OK": "Lần kiểm tra cuối: Khóa API hợp lệ",
|
|
1265
|
+
"Último teste: falta configuração": "Lần kiểm tra cuối: Thiếu cấu hình",
|
|
1266
|
+
"Último teste: requer autorização ou endpoint seguro": "Lần kiểm tra cuối: Cần ủy quyền bổ sung hoặc endpoint an toàn",
|
|
1267
|
+
"Último teste: chamada falhou": "Lần kiểm tra cuối: Lệnh gọi thất bại",
|
|
1268
|
+
"Testar credenciais TTS e música": "Kiểm tra thông tin xác thực TTS và âm nhạc",
|
|
1269
|
+
},
|
|
1270
|
+
"tr": {
|
|
1271
|
+
"A testar chamada API…": "API çağrısı test ediliyor…",
|
|
1272
|
+
"Último teste: API Key OK": "Son test: API anahtarı OK",
|
|
1273
|
+
"Último teste: falta configuração": "Son test: yapılandırma eksik",
|
|
1274
|
+
"Último teste: requer autorização ou endpoint seguro": "Son test: ek yetkilendirme veya güvenli uç nokta gerekli",
|
|
1275
|
+
"Último teste: chamada falhou": "Son test: çağrı başarısız",
|
|
1276
|
+
"Testar credenciais TTS e música": "TTS ve müzik kimlik bilgilerini test et",
|
|
1277
|
+
},
|
|
1278
|
+
"ru": {
|
|
1279
|
+
"A testar chamada API…": "Проверка вызова API…",
|
|
1280
|
+
"Último teste: API Key OK": "Последняя проверка: API-ключ действителен",
|
|
1281
|
+
"Último teste: falta configuração": "Последняя проверка: не хватает конфигурации",
|
|
1282
|
+
"Último teste: requer autorização ou endpoint seguro": "Последняя проверка: требуется дополнительная авторизация или безопасный эндпоинт",
|
|
1283
|
+
"Último teste: chamada falhou": "Последняя проверка: вызов не выполнен",
|
|
1284
|
+
"Testar credenciais TTS e música": "Проверить учётные данные TTS и музыки",
|
|
1285
|
+
},
|
|
1286
|
+
"es": {
|
|
1287
|
+
"A testar chamada API…": "Probando la llamada API…",
|
|
1288
|
+
"Último teste: API Key OK": "Última prueba: clave API correcta",
|
|
1289
|
+
"Último teste: falta configuração": "Última prueba: falta configuración",
|
|
1290
|
+
"Último teste: requer autorização ou endpoint seguro": "Última prueba: se requiere autorización adicional o un endpoint seguro",
|
|
1291
|
+
"Último teste: chamada falhou": "Última prueba: la llamada falló",
|
|
1292
|
+
"Testar credenciais TTS e música": "Probar credenciales de TTS y música",
|
|
1293
|
+
},
|
|
1294
|
+
"id": {
|
|
1295
|
+
"A testar chamada API…": "Menguji panggilan API…",
|
|
1296
|
+
"Último teste: API Key OK": "Tes terakhir: Kunci API OK",
|
|
1297
|
+
"Último teste: falta configuração": "Tes terakhir: konfigurasi tidak lengkap",
|
|
1298
|
+
"Último teste: requer autorização ou endpoint seguro": "Tes terakhir: diperlukan otorisasi tambahan atau endpoint aman",
|
|
1299
|
+
"Último teste: chamada falhou": "Tes terakhir: panggilan gagal",
|
|
1300
|
+
"Testar credenciais TTS e música": "Uji kredensial TTS dan musik",
|
|
1301
|
+
},
|
|
1302
|
+
"it": {
|
|
1303
|
+
"A testar chamada API…": "Test della chiamata API in corso…",
|
|
1304
|
+
"Último teste: API Key OK": "Ultimo test: chiave API OK",
|
|
1305
|
+
"Último teste: falta configuração": "Ultimo test: configurazione mancante",
|
|
1306
|
+
"Último teste: requer autorização ou endpoint seguro": "Ultimo test: è necessaria un'autorizzazione aggiuntiva o un endpoint sicuro",
|
|
1307
|
+
"Último teste: chamada falhou": "Ultimo test: chiamata non riuscita",
|
|
1308
|
+
"Testar credenciais TTS e música": "Testa le credenziali TTS e musica",
|
|
1309
|
+
},
|
|
1310
|
+
}
|
|
1311
|
+
for _language_code, _api_test_values in _API_TEST_TRANSLATIONS.items():
|
|
1312
|
+
UI_TRANSLATIONS[_language_code].update(_api_test_values)
|
package/package.json
CHANGED