From 44a0be36f7d1b7a68ba51ebc73aa6deb2fa50d6f Mon Sep 17 00:00:00 2001 From: Hoseok Seo Date: Sun, 29 Sep 2024 12:16:19 +0000 Subject: [PATCH 01/61] Translated using Weblate (Korean) Currently translated at 100.0% (3223 of 3223 strings) Translation: Weblate/Application Translate-URL: https://hosted.weblate.org/projects/weblate/application/ko/ --- weblate/locale/ko/LC_MESSAGES/django.po | 71 ++++++++----------------- 1 file changed, 22 insertions(+), 49 deletions(-) diff --git a/weblate/locale/ko/LC_MESSAGES/django.po b/weblate/locale/ko/LC_MESSAGES/django.po index 567fb77f25e9..d1cd7d73ed35 100644 --- a/weblate/locale/ko/LC_MESSAGES/django.po +++ b/weblate/locale/ko/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ msgstr "" "Project-Id-Version: Weblate 5.8\n" "Report-Msgid-Bugs-To: https://github.com/WeblateOrg/weblate/issues\n" "POT-Creation-Date: 2024-09-20 12:26+0000\n" -"PO-Revision-Date: 2024-09-29 12:15+0000\n" +"PO-Revision-Date: 2024-09-30 13:16+0000\n" "Last-Translator: Hoseok Seo \n" "Language-Team: Korean \n" @@ -5126,16 +5126,16 @@ msgstr "애저 OpenAI 엔드포인트 URL" #: weblate/machinery/forms.py:407 msgid "" "Endpoint URL of the instance, e.g: https://my-instance.openai.azure.com." -msgstr "" +msgstr "인스턴스의 엔드포인트 URL, 예: https://my-instance.openai.azure.com." #: weblate/machinery/forms.py:413 msgctxt "Automatic suggestion service configuration" msgid "Azure OpenAI deployment" -msgstr "" +msgstr "애저 OpenAI 배포" #: weblate/machinery/forms.py:416 msgid "The model's unique deployment name." -msgstr "" +msgstr "모델의 고유한 배포 이름입니다." #: weblate/machinery/views.py:404 msgid "Service is currently not available." @@ -5355,48 +5355,32 @@ msgid "CSRF verification failed. Request aborted." msgstr "CSRF 확인에 실패했습니다. 요청을 중단하였습니다." #: weblate/templates/403_csrf.html:15 -#, fuzzy -#| msgid "" -#| "This HTTPS site requires a 'Referer' header to be sent by your web " -#| "browser, but none was sent. This header is required for security reasons, " -#| "to ensure your browser is not hijacked by third-parties." msgid "" "This HTTPS site requires a 'Referer' header to be sent by your web browser, " "but none was sent. This header is required for security reasons, ensuring " "that your browser is not being hijacked by third parties." msgstr "" -"이 HTTPS 사이트를 사용하려면 웹 브라우저에서 'Referer' 헤더를 보내야 하지만 " -"아무 것도 보내지 않았습니다. 이 헤더는 보안상의 이유로 브라우저가 제3자에 의" -"해 해킹되지 않도록 하기 위해 필요합니다." +"이 HTTPS 사이트는 웹 브라우저에서 'Referer' 헤더를 보내야 하지만 아무것도 " +"보내지지 않았습니다. 이 헤더는 보안상의 이유로 필요하며, 브라우저가 " +"제3자에게 하이재킹되지 않도록 보장합니다." #: weblate/templates/403_csrf.html:16 -#, fuzzy -#| msgid "" -#| "If you have set up your web browser to not send 'Referer' headers, please " -#| "turn that on (at-least for this site, for HTTPS connections, or for 'same-" -#| "origin' requests)." msgid "" "If you have set up your web browser to not send 'Referer' headers, you need " "to turn that on (at least for this site, HTTPS connections, or 'same-origin' " "requests)." msgstr "" -"웹 브라우저가 'Referer' 헤더를 보내지 않도록 설정했다면 이 기능을 활성화해야 " -"합니다 (적어도 이 사이트, 보안 HTTPS 연결 또는 'same-origin' 요청의 경우)." +"웹 브라우저에서 'Referer' 헤더를 보내지 않도록 설정한 경우, 해당 헤더를 " +"켜야 합니다 (적어도 이 사이트의 경우 HTTPS 연결 또는 'same-origin' 요청)." #: weblate/templates/403_csrf.html:18 -#, fuzzy -#| msgid "" -#| "This site requires a CSRF cookie when submitting forms. This cookie is " -#| "required for security reasons, to ensure that your browser is not being " -#| "hijacked by third-parties." msgid "" "Our site requires a session cookie to make the forms work. This cookie is " "required for security reasons, ensuring that your browser is not being " "hijacked by third parties." msgstr "" -"사이트가 폼을 제출할 때 CSRF 쿠키를 필요로 합니다. 이 쿠키는 보안상의 이유로 " -"필요하며, 제3자에 의해 당신의 브라우저가 해킹당하고 있지 않다는 것을 보장합니" -"다." +"사이트는 양식을 작동시키기 위해 세션 쿠키가 필요합니다. 이 쿠키는 보안상의 " +"이유로 필요하며, 브라우저가 제3자에게 하이재킹되지 않도록 보장합니다." #: weblate/templates/403_csrf.html:19 msgid "" @@ -5404,18 +5388,17 @@ msgid "" "This can easily happen for unauthenticated requests where session validity " "is limited. You might want to try your intended action again in such a case." msgstr "" +"세션 쿠키가 비활성으로 인해 만료되었을 수 있습니다. 이는 세션 유효성이 " +"제한된 인증되지 않은 요청에서 쉽게 발생할 수 있습니다. 그런 경우 의도한 " +"작업을 다시 시도하는 것이 좋습니다." #: weblate/templates/403_csrf.html:20 -#, fuzzy -#| msgid "" -#| "If you have set up your browser to not save cookies, please turn them on " -#| "again (at-least for this site, or for 'same-origin' requests)." msgid "" "If you have set up your browser to not save cookies, you need to enable " "saving them (at least for this site, or 'same-origin' requests)." msgstr "" -"쿠키를 저장하지 않도록 브라우저를 설정했다면 쿠키를 다시 켜십시오 (적어도 이 " -"사이트 또는 'same-origin' 요청에 대해)." +"브라우저에서 쿠키를 저장하지 않도록 설정한 경우, 쿠키 저장을 허용해야 합니다 " +"(적어도 이 사이트나 'same-origin' 요청의 경우)." #: weblate/templates/403_csrf.html:22 #, python-format @@ -6519,8 +6502,6 @@ msgid "User ID" msgstr "사용자 ID" #: weblate/templates/accounts/user.html:236 -#, fuzzy -#| msgid "Notifications" msgid "Notification" msgstr "알림" @@ -6530,7 +6511,7 @@ msgstr "범위" #: weblate/templates/accounts/user.html:238 msgid "Frequency" -msgstr "" +msgstr "빈도" #: weblate/templates/accounts/user.html:241 msgid "One-time" @@ -6672,10 +6653,8 @@ msgid "Configure add-on" msgstr "애드온 구성하기" #: weblate/templates/addons/addon_detail.html:44 -#, fuzzy -#| msgid "This add-on executes a script." msgid "This add-on has no settings." -msgstr "이 애드온은 스크립트를 실행합니다." +msgstr "이 추가 기능에는 설정이 없습니다." #: weblate/templates/addons/addon_head.html:7 #: weblate/templates/addons/addon_list.html:86 @@ -9647,10 +9626,8 @@ msgid "Delete announcement" msgstr "공지사항 삭제" #: weblate/templates/multi-progress.html:15 -#, fuzzy -#| msgid "Component is being updated…" msgid "Components are being updated…" -msgstr "구성요소 업데이트 중…" +msgstr "구성 요소가 업데이트되고 있습니다…" #: weblate/templates/new-language.html:25 msgid "" @@ -9808,16 +9785,12 @@ msgid "Please try again later." msgstr "잠시 후 다시 시도하시기 바랍니다." #: weblate/templates/registration/logged_out.html:14 -#, fuzzy -#| msgid "Thank you for using Weblate." msgid "Thank you for using Weblate" -msgstr "웹레이트를 이용해 주셔서 감사합니다." +msgstr "웹레이트를 사용해주셔서 감사합니다" #: weblate/templates/registration/logged_out.html:17 -#, fuzzy -#| msgid "Show on dashboard" msgid "Return to dashboard" -msgstr "참여 프로젝트에 표시하기" +msgstr "대시보드로 돌아가기" #: weblate/templates/replace.html:13 msgid "Please review and confirm the search and replace results." @@ -10707,7 +10680,7 @@ msgstr "지난 달에 추가된 미번역된 모든 문자열" #: weblate/templates/snippets/query-builder.html:111 msgid "Filter changed strings 2 weeks ago" -msgstr "" +msgstr "필터가 2주 전에 문자열을 변경했음" #: weblate/templates/snippets/query-builder.html:118 msgid "Translated strings in a certain language" From 8c8de64299aa8d6d5ddaa4ddf5b13bbbeb252eed Mon Sep 17 00:00:00 2001 From: Reno Tx Date: Sun, 29 Sep 2024 18:53:28 +0000 Subject: [PATCH 02/61] Translated using Weblate (Serbian) Currently translated at 66.7% (2151 of 3223 strings) Translation: Weblate/Application Translate-URL: https://hosted.weblate.org/projects/weblate/application/sr/ --- weblate/locale/sr/LC_MESSAGES/django.po | 972 +++++++++++++++--------- 1 file changed, 610 insertions(+), 362 deletions(-) diff --git a/weblate/locale/sr/LC_MESSAGES/django.po b/weblate/locale/sr/LC_MESSAGES/django.po index 821f052a99f0..21a142b1f59b 100644 --- a/weblate/locale/sr/LC_MESSAGES/django.po +++ b/weblate/locale/sr/LC_MESSAGES/django.po @@ -12,8 +12,8 @@ msgstr "" "Project-Id-Version: Weblate 5.8\n" "Report-Msgid-Bugs-To: https://github.com/WeblateOrg/weblate/issues\n" "POT-Creation-Date: 2024-09-20 12:26+0000\n" -"PO-Revision-Date: 2024-09-20 19:19+0000\n" -"Last-Translator: Anonymous \n" +"PO-Revision-Date: 2024-09-30 13:16+0000\n" +"Last-Translator: Reno Tx \n" "Language-Team: Serbian \n" "Language: sr\n" @@ -786,10 +786,13 @@ msgstr "Предузеће" msgid "" "Please choose which component list you want to display on the dashboard." msgstr "" +"Молимо вас да изаберете коју листу компоненти желите да прикажете на " +"контролној табли." #: weblate/accounts/models.py:740 msgid "Selecting component list has no effect when not shown on the dashboard." msgstr "" +"Избор листе компоненти нема ефекта када није приказан на контролној табли." #: weblate/accounts/models.py:873 weblate/trans/views/edit.py:373 #, python-format @@ -961,6 +964,7 @@ msgstr "Не можете поново користити исту лозинк #: weblate/accounts/password_validation.py:61 msgid "Your password can't match a password you have used in the past." msgstr "" +"Ваша лозинка не може бити иста као лозинка коју сте користили у прошлости." #: weblate/accounts/pipeline.py:351 msgid "You can not complete password reset while signed in." @@ -1115,6 +1119,7 @@ msgstr "Не могу да уклоним идентитет корисника" #: weblate/accounts/views.py:1291 msgid "Add another identity by confirming your e-mail address first." msgstr "" +"Додајте други идентитет тако што ћете прво потврдити своју адресу е-поште." #: weblate/accounts/views.py:1335 #, fuzzy, python-format @@ -1142,7 +1147,7 @@ msgstr "" #: weblate/accounts/views.py:1370 msgid "Could not authenticate due to invalid session state." -msgstr "" +msgstr "Нисам могао да се аутентификујем због неважећег стања сесије." #: weblate/accounts/views.py:1379 msgid "Got no e-mail address from third party authentication service." @@ -1160,6 +1165,8 @@ msgstr "Нове регистрације су искључене." msgid "" "Could not authenticate, probably due to an expired token or connection error." msgstr "" +"Нисам могао да се аутентификујем, вероватно због истеклог токена или грешке " +"у вези." #: weblate/accounts/views.py:1458 msgid "Authentication cancelled." @@ -1167,7 +1174,7 @@ msgstr "Аутентификација је отказана." #: weblate/accounts/views.py:1461 msgid "The server does not allow authentication." -msgstr "" +msgstr "Сервер не дозвољава аутентификацију." #: weblate/accounts/views.py:1466 msgid "The supplied e-mail address is already in use for another account." @@ -1190,6 +1197,8 @@ msgid "" "The notification change link is no longer valid, please sign in to configure " "notifications." msgstr "" +"Линк за промену обавештења више није валидан, молимо пријавите се да " +"конфигуришете обавештења." #: weblate/accounts/views.py:1649 #, fuzzy @@ -1229,16 +1238,19 @@ msgid "" "Automatically translates strings using machine translation or other " "components." msgstr "" +"Аутоматски преводи низове користећи машински превод или друге компоненте." #: weblate/addons/base.py:398 weblate/trans/views/create.py:251 msgid "" "The repository is outdated, you might not get expected results until you " "update it." msgstr "" +"Складиште је застарело, можда нећете добити очекиване резултате док га не " +"ажурирате." #: weblate/addons/cdn.py:35 msgid "JavaScript localization CDN" -msgstr "" +msgstr "JavaScript локализациони CDN" #: weblate/addons/cdn.py:37 msgid "" @@ -1256,6 +1268,9 @@ msgid "" "file formats, this means removing stale translation keys no longer present " "in the base file." msgstr "" +"Ажурирајте све преводе да одговарају једнојезичном основном фајлу. За већину " +"формата фајлова, ово значи уклањање застарелих преводних кључева који више " +"нису присутни у основном фајлу." #: weblate/addons/cleanup.py:75 msgid "Remove blank strings" @@ -1286,6 +1301,8 @@ msgid "" "Automatically adds or removes project components based on file changes in " "the version control system." msgstr "" +"Аутоматски додаје или уклања компоненте пројекта на основу промена фајлова у " +"систему контроле верзија." #: weblate/addons/example.py:20 #, fuzzy @@ -1317,6 +1334,9 @@ msgid "" "needing editing in Weblate. This way you can easily filter and edit source " "strings written by the developers." msgstr "" +"Када год се нова изворна ниска увезе из VCS-а, означава се као потребна за " +"уређивање у Веблејту. На овај начин можете лако филтрирати и уредити изворне " +"ниске које су написали програмери." #: weblate/addons/flags.py:57 msgid "Flag new translations as \"Needs editing\"" @@ -1328,6 +1348,9 @@ msgid "" "as needing editing in Weblate. This way you can easily filter and edit " "translations created by the developers." msgstr "" +"Када год се нова преводива ниска увезе из VCS-а, означава се као потребна за " +"уређивање у Веблејту. На овај начин можете лако филтрирати и уредити преводе " +"које су креирали програмери." #: weblate/addons/flags.py:75 msgid "Flag unchanged translations as \"Needs editing\"" @@ -1361,7 +1384,7 @@ msgstr "Путања направљеног МО фајла" #: weblate/addons/forms.py:52 msgid "If not specified, the location of the PO file will be used." -msgstr "" +msgstr "Ако није наведено, користиће се локација PO датотеке." #: weblate/addons/forms.py:56 #, fuzzy @@ -1419,15 +1442,15 @@ msgstr "Уклони локације преведених ниски" #: weblate/addons/forms.py:154 msgid "Use fuzzy matching" -msgstr "" +msgstr "Користите фази подударање" #: weblate/addons/forms.py:160 msgid "Commit squashing" -msgstr "" +msgstr "Спајање комита" #: weblate/addons/forms.py:163 msgid "All commits into one" -msgstr "" +msgstr "Сви комити у један" #: weblate/addons/forms.py:164 msgid "Per language" @@ -1443,7 +1466,7 @@ msgstr "По аутору" #: weblate/addons/forms.py:172 msgid "Append trailers to squashed commit message" -msgstr "" +msgstr "Додајте приколице у поруку урезивања" #: weblate/addons/forms.py:176 msgid "" @@ -1463,6 +1486,8 @@ msgid "" "This commit message will be used instead of the combined commit messages " "from the squashed commits." msgstr "" +"Ова порука урезивања ће се користити уместо комбинованих порука урезивања из " +"спојених урезивања." #: weblate/addons/forms.py:203 msgid "Sort JSON keys" @@ -1528,7 +1553,7 @@ msgstr "МЕК (\\r)" #: weblate/addons/forms.py:262 msgid "Days to keep" -msgstr "" +msgstr "Дани за чување" #: weblate/addons/forms.py:268 msgid "Voting threshold" @@ -1536,7 +1561,7 @@ msgstr "Праг гласања" #: weblate/addons/forms.py:272 msgid "Threshold for removal. This field has no effect with voting turned off." -msgstr "" +msgstr "Праг за уклањање. Ово поље нема ефекта ако је гласање искључено." #: weblate/addons/forms.py:279 msgid "Regular expression to match translation files against" @@ -1555,7 +1580,7 @@ msgstr "Прилагоди назив компоненте" #: weblate/addons/forms.py:294 msgid "Define the monolingual base filename" -msgstr "" +msgstr "Дефиниши једнојезични основни назив фајла" #: weblate/addons/forms.py:297 msgid "Leave empty for bilingual translation files." @@ -1570,6 +1595,8 @@ msgid "" "Filename of file used for creating new translations. For gettext choose .pot " "file." msgstr "" +"Назив фајла који се користи за креирање нових превода. За gettext изаберите ." +"pot фајл." #: weblate/addons/forms.py:309 #: weblate/templates/trans/alert/inexistantfiles.html:13 @@ -1583,6 +1610,9 @@ msgid "" "translation file provided by developers and is used when creating actual " "source strings." msgstr "" +"Име фајла посредног преводног фајла. У већини случајева ово је преводни фајл " +"који обезбеђују програмери и користи се приликом креирања стварних изворних " +"низова." #: weblate/addons/forms.py:320 weblate/trans/models/component.py:709 msgid "Language filter" @@ -1608,11 +1638,11 @@ msgstr "" #: weblate/addons/forms.py:340 msgid "I confirm the above matches look correct" -msgstr "" +msgstr "Потврђујем да горе наведена подударања изгледају исправно" #: weblate/addons/forms.py:397 weblate/trans/models/component.py:2959 msgid "You can not use a monolingual translation without a base file." -msgstr "" +msgstr "Не можете користити монолингвални превод без базне датотеке." #: weblate/addons/forms.py:405 weblate/trans/models/component.py:2893 msgid "You can not use a base file for bilingual translation." @@ -1620,11 +1650,11 @@ msgstr "Не можете кориситити основни фајл за дв #: weblate/addons/forms.py:419 msgid "Please review and confirm the matched components." -msgstr "" +msgstr "Молимо прегледајте и потврдите подударне компоненте." #: weblate/addons/forms.py:447 msgid "Please include component markup in the template." -msgstr "" +msgstr "Молимо вас да укључите ознаку компоненте у шаблон." #: weblate/addons/forms.py:496 msgid "Translation threshold" @@ -1636,11 +1666,11 @@ msgstr "Праг након којег се преводи користе." #: weblate/addons/forms.py:504 msgid "CSS selector" -msgstr "" +msgstr "CSS селектор" #: weblate/addons/forms.py:507 msgid "CSS selector to detect localizable elements." -msgstr "" +msgstr "CSS селектор за детекцију локализованих елемената." #: weblate/addons/forms.py:510 msgid "Language cookie name" @@ -1648,7 +1678,7 @@ msgstr "Назив колачића језика" #: weblate/addons/forms.py:513 msgid "Name of cookie which stores language preference." -msgstr "" +msgstr "Име колачића који чува језичке преференције." #: weblate/addons/forms.py:518 msgid "Extract strings from HTML files" @@ -1659,6 +1689,8 @@ msgid "" "List of filenames in current repository or remote URLs to parse for " "translatable strings." msgstr "" +"Листа имена фајлова у тренутном складишту или удаљених URL-ова за парсирање " +"преводивих низова." #: weblate/addons/forms.py:549 #, fuzzy, python-format @@ -1778,7 +1810,7 @@ msgstr "Генериши МО фајлове" #: weblate/addons/gettext.py:35 msgid "Automatically generates a MO file for every changed PO file." -msgstr "" +msgstr "Аутоматски генерише MO фајл за сваки промењени PO фајл." #: weblate/addons/gettext.py:66 msgid "Update LINGUAS file" @@ -1786,17 +1818,19 @@ msgstr "Ажурирај „LINGUAS“ фајл" #: weblate/addons/gettext.py:68 msgid "Updates the LINGUAS file when a new translation is added." -msgstr "" +msgstr "Ажурира LINGUAS фајл када се дода нови превод." #: weblate/addons/gettext.py:159 msgid "Update ALL_LINGUAS variable in the \"configure\" file" -msgstr "" +msgstr "Ажурирајте ALL_LINGUAS променљиву у \"configure\" датотеци" #: weblate/addons/gettext.py:161 msgid "" "Updates the ALL_LINGUAS variable in \"configure\", \"configure.in\" or " "\"configure.ac\" files, when a new translation is added." msgstr "" +"Ажурира променљиву ALL_LINGUAS у \"configure\", \"configure.in\" или " +"\"configure.ac\" датотекама, када се дода нови превод." #: weblate/addons/gettext.py:230 msgid "Update PO files to match POT (msgmerge)" @@ -1810,12 +1844,13 @@ msgstr "" #: weblate/addons/gettext.py:326 msgid "Customize gettext output" -msgstr "" +msgstr "Прилагоди gettext излаз" #: weblate/addons/gettext.py:328 msgid "" "Allows customization of gettext output behavior, for example line wrapping." msgstr "" +"Омогућава прилагођавање понашања gettext излаза, на пример преламање линија." #: weblate/addons/gettext.py:348 msgid "Contributors in comment" @@ -1829,20 +1864,21 @@ msgstr "" #: weblate/addons/git.py:25 msgid "Squash Git commits" -msgstr "" +msgstr "Споји Git комите" #: weblate/addons/git.py:26 msgid "Squash Git commits prior to pushing changes." -msgstr "" +msgstr "Споји Git комите пре слања промена." #: weblate/addons/json.py:13 msgid "Customize JSON output" -msgstr "" +msgstr "Прилагоди JSON излаз" #: weblate/addons/json.py:15 msgid "" "Allows adjusting JSON output behavior, for example indentation or sorting." msgstr "" +"Омогућава подешавање понашања JSON излаза, на пример увлачење или сортирање." #: weblate/addons/properties.py:140 #, fuzzy @@ -1860,7 +1896,7 @@ msgstr "Уклањање старих коментара" #: weblate/addons/removal.py:36 msgid "Set a timeframe for removal of comments." -msgstr "" +msgstr "Поставите временски оквир за уклањање коментара." #: weblate/addons/removal.py:49 msgid "Stale suggestion removal" @@ -1868,7 +1904,7 @@ msgstr "Уклањање старих предлога" #: weblate/addons/removal.py:50 msgid "Set a timeframe for removal of suggestions." -msgstr "" +msgstr "Поставите временски оквир за уклањање сугестија." #: weblate/addons/resx.py:22 msgid "Update RESX files" @@ -1880,6 +1916,9 @@ msgid "" "Unused strings are removed, and new ones added as copies of the source " "string." msgstr "" +"Ажурирајте све преводне фајлове да одговарају једнојезичном узводном базном " +"фајлу. Неискоришћени низови се уклањају, а нови се додају као копије " +"изворног низа." #: weblate/addons/views.py:126 #, python-format @@ -1900,12 +1939,14 @@ msgstr "" #: weblate/addons/yaml.py:15 msgid "Customize YAML output" -msgstr "" +msgstr "Прилагодите YAML излаз" #: weblate/addons/yaml.py:17 msgid "" "Allows adjusting YAML output behavior, for example line-length or newlines." msgstr "" +"Омогућава подешавање понашања YAML излаза, на пример дужину линије или нове " +"линије." #: weblate/api/views.py:1921 weblate/auth/models.py:921 #: weblate/checks/views.py:228 weblate/checks/views.py:235 @@ -2126,7 +2167,7 @@ msgstr "Одобрава ниске" #. Translators: Permission name #: weblate/auth/data.py:75 msgid "Edit string when suggestions are enforced" -msgstr "" +msgstr "Urediti ниску када су предлози обавезни" #. Translators: Permission name #: weblate/auth/data.py:77 @@ -2218,7 +2259,7 @@ msgstr "Поништава измене у локалном складишту" #. Translators: Permission name #: weblate/auth/data.py:111 msgid "View upstream repository location" -msgstr "" +msgstr "Погледај локацију узводног складишта" #. Translators: Permission name #: weblate/auth/data.py:113 @@ -2228,7 +2269,7 @@ msgstr "Ажурира локално складиште" #. Translators: Permission name #: weblate/auth/data.py:121 msgid "Use management interface" -msgstr "" +msgstr "Користите интерфејс за управљање" #. Translators: Permission name #: weblate/auth/data.py:123 @@ -2636,7 +2677,7 @@ msgstr "Активно" #: weblate/auth/models.py:426 msgid "Mark user as inactive instead of removing." -msgstr "" +msgstr "Означи корисника као неактивног уместо уклањања." #: weblate/auth/models.py:434 weblate/templates/trans/project-access.html:283 #, fuzzy @@ -2690,7 +2731,7 @@ msgstr "Корисник за додавање" #: weblate/auth/models.py:1095 weblate/trans/forms.py:1259 #: weblate/trans/forms.py:1277 msgid "Please type in an existing Weblate account name or e-mail address." -msgstr "" +msgstr "Молимо унесите постојеће име налога на Веблејту или адресу е-поште." #: weblate/auth/models.py:1103 msgid "Suggest username for the user. It can be changed later." @@ -2762,7 +2803,7 @@ msgstr "" #: weblate/auth/permissions.py:233 #: weblate/templates/snippets/project/state.html:6 msgid "Pay the bills to unlock this project." -msgstr "" +msgstr "Платите рачуне да бисте откључали овај пројекат." #: weblate/auth/permissions.py:246 msgid "Source string reviews are not enabled." @@ -2795,6 +2836,8 @@ msgid "" "Only reviewers can change approved strings, please add a suggestion if you " "think the string should be changed." msgstr "" +"Само рецензенти могу мењати одобрене ниске, молимо додајте предлог ако " +"мислите да би ниска требало да се промени." #: weblate/auth/permissions.py:290 #, fuzzy @@ -2878,7 +2921,7 @@ msgstr "Стање наплате" #: weblate/billing/models.py:175 weblate/templates/billing/list.html:13 #: weblate/templates/billing/status.html:27 msgid "Trial expiry date" -msgstr "" +msgstr "Датум истека пробног периода" #: weblate/billing/models.py:181 weblate/templates/billing/list.html:16 #: weblate/templates/billing/state-snippet.html:13 @@ -2958,6 +3001,8 @@ msgid "" "You will stop receiving this notification once you pay the bills or the " "project is removed." msgstr "" +"Престаћете да примате ово обавештење када платите рачуне или пројекат буде " +"уклоњен." #: weblate/billing/tasks.py:74 msgid "" @@ -3129,7 +3174,7 @@ msgstr "Размак интерпункције" #: weblate/checks/chars.py:453 msgid "Missing non breakable space before double punctuation sign" -msgstr "" +msgstr "Недостаје непрекидна размака пре знака двоструке интерпункције" #: weblate/checks/consistency.py:26 msgid "Missing plurals" @@ -3323,7 +3368,7 @@ msgstr "Фајл фонта" #: weblate/checks/flags.py:294 #, python-format msgid "Invalid translation flag: \"%s\"" -msgstr "" +msgstr "Неважећа застава превода: \"%s\"" #: weblate/checks/flags.py:298 #, fuzzy, python-format @@ -3675,7 +3720,7 @@ msgstr "Python формат" #: weblate/checks/format.py:508 msgid "Python format string does not match source" -msgstr "" +msgstr "Python формат низ не одговара извору" #: weblate/checks/format.py:516 msgid "PHP format" @@ -3715,11 +3760,11 @@ msgstr "Перл ознаке у преводу не одговарају изв #: weblate/checks/format.py:553 msgid "JavaScript format" -msgstr "" +msgstr "JavaScript формат" #: weblate/checks/format.py:554 msgid "JavaScript format string does not match source" -msgstr "" +msgstr "JavaScript формат низ не одговара извору" #: weblate/checks/format.py:561 msgid "Lua format" @@ -3759,7 +3804,7 @@ msgstr "Python формат са заградама" #: weblate/checks/format.py:591 msgid "Python brace format string does not match source" -msgstr "" +msgstr "Python формат са заградама не одговара извору" #: weblate/checks/format.py:611 msgid "Single {} encountered in the format string." @@ -3791,23 +3836,23 @@ msgstr "Ознаке за Јава поруке у преводу не одго #: weblate/checks/format.py:701 msgid "You need to pair up an apostrophe with another one." -msgstr "" +msgstr "Потребно је да упарите апостроф са другим." #: weblate/checks/format.py:707 msgid "i18next interpolation" -msgstr "" +msgstr "i18next интерполација" #: weblate/checks/format.py:708 msgid "The i18next interpolation does not match source" -msgstr "" +msgstr "i18next интерполација не одговара извору" #: weblate/checks/format.py:721 msgid "ECMAScript template literals" -msgstr "" +msgstr "ECMAScript шаблонски литерали" #: weblate/checks/format.py:722 msgid "ECMAScript template literals do not match source" -msgstr "" +msgstr "ECMAScript шаблонски литерали не одговарају извору" #: weblate/checks/format.py:735 msgid "Percent placeholders" @@ -3827,13 +3872,15 @@ msgstr "" #: weblate/checks/format.py:752 msgid "Multiple unnamed variables" -msgstr "" +msgstr "Више неименованих варијабли" #: weblate/checks/format.py:754 msgid "" "There are multiple unnamed variables in the string, making it impossible for " "translators to reorder them" msgstr "" +"Постоји више неименованих варијабли у низу, што онемогућава преводиоцима да " +"их преуреде" #: weblate/checks/glossary.py:22 msgid "Does not follow glossary" @@ -3960,23 +4007,23 @@ msgstr "Маркдаун смернице" #: weblate/checks/markup.py:236 msgid "Markdown link references do not match source" -msgstr "" +msgstr "Markdown референце линкова се не подударају са извором" #: weblate/checks/markup.py:252 msgid "Markdown links" -msgstr "" +msgstr "Markdown везе" #: weblate/checks/markup.py:253 msgid "Markdown links do not match source" -msgstr "" +msgstr "Markdown везе не одговарају извору" #: weblate/checks/markup.py:281 msgid "Markdown syntax" -msgstr "" +msgstr "Markdown синтакса" #: weblate/checks/markup.py:282 msgid "Markdown syntax does not match source" -msgstr "" +msgstr "Markdown синтакса не одговара извору" #: weblate/checks/markup.py:315 msgid "The translation does not contain an URL" @@ -3984,7 +4031,7 @@ msgstr "Превод не садржи УРЛ" #: weblate/checks/markup.py:334 weblate/trans/autofixes/html.py:16 msgid "Unsafe HTML" -msgstr "" +msgstr "Небезбедан HTML" #: weblate/checks/markup.py:335 msgid "The translation uses unsafe HTML markup" @@ -3992,7 +4039,7 @@ msgstr "Превод користи небезбедно ХТМЛ означав #: weblate/checks/placeholders.py:31 msgid "Placeholders" -msgstr "" +msgstr "Замене" #: weblate/checks/placeholders.py:32 msgid "Translation is missing some placeholders" @@ -4061,7 +4108,7 @@ msgstr "Без множине" #: weblate/checks/source.py:33 msgid "The string is used as plural, but not using plural forms" -msgstr "" +msgstr "Ниска се користи као множина, али не користи облике множине" #: weblate/checks/source.py:46 msgid "Ellipsis" @@ -4089,7 +4136,7 @@ msgstr "Дуго непреведено" #: weblate/checks/source.py:108 msgid "The string has not been translated for a long time" -msgstr "" +msgstr "Ниска није преведена дуго времена" #: weblate/checks/views.py:218 weblate/templates/category-project.html:44 #: weblate/templates/category.html:68 weblate/templates/component.html:75 @@ -4124,7 +4171,7 @@ msgstr "Веблејт поставка" #: weblate/fonts/models.py:30 weblate/templates/fonts/font_detail.html:19 #: weblate/templates/fonts/font_list.html:74 msgid "Font style" -msgstr "" +msgstr "Стил фонта" #: weblate/fonts/models.py:33 msgid "Font file" @@ -4132,17 +4179,19 @@ msgstr "Фајл фонта" #: weblate/fonts/models.py:36 msgid "OpenType and TrueType fonts are supported." -msgstr "" +msgstr "OpenType и TrueType фонтови су подржани." #: weblate/fonts/models.py:103 msgid "Font group name" -msgstr "" +msgstr "Име групе фонта" #: weblate/fonts/models.py:106 msgid "" "Identifier you will use in checks to select this font group. Avoid " "whitespaces and special characters." msgstr "" +"Идентификатор који ћете користити у проверама за избор ове групе фонтова. " +"Избегавајте размака и специјалне знакове." #: weblate/fonts/models.py:112 weblate/templates/fonts/font_list.html:26 #: weblate/templates/fonts/fontgroup_detail.html:19 @@ -4152,15 +4201,16 @@ msgstr "Подразумевани фонт" #: weblate/fonts/models.py:115 msgid "Default font is used unless per language override matches." msgstr "" +"Подразумевани фонт се користи осим ако постоји посебно подешавање по језику." #: weblate/fonts/models.py:143 weblate/templates/fonts/font_detail.html:16 msgid "Font" -msgstr "" +msgstr "Фонт" #: weblate/fonts/validators.py:17 weblate/fonts/validators.py:21 #: weblate/utils/validators.py:163 msgid "Unsupported file format." -msgstr "" +msgstr "Неподржан формат фајла." #: weblate/fonts/views.py:71 #, fuzzy @@ -4184,11 +4234,11 @@ msgstr "Унос са истим називом већ постоји." #: weblate/fonts/views.py:169 msgid "No override found." -msgstr "" +msgstr "Нисам пронашао замену." #: weblate/fonts/views.py:174 msgid "Font group deleted." -msgstr "" +msgstr "Група фонта је обрисана." #: weblate/formats/base.py:363 msgid "Could not load strings from the file, try choosing other format." @@ -4214,7 +4264,7 @@ msgstr "ИДМЛ фајл" #: weblate/formats/convert.py:467 msgid "RC file" -msgstr "" +msgstr "RC фајл" #: weblate/formats/convert.py:534 #, fuzzy @@ -4240,15 +4290,15 @@ msgstr "" #: weblate/formats/exporters.py:126 msgid "XLIFF 1.1" -msgstr "" +msgstr "XLIFF 1.1" #: weblate/formats/exporters.py:134 msgid "TBX" -msgstr "" +msgstr "TBX" #: weblate/formats/exporters.py:142 msgid "TMX" -msgstr "" +msgstr "TMX" #: weblate/formats/exporters.py:150 msgid "gettext MO" @@ -4268,7 +4318,7 @@ msgstr "JSON" #: weblate/formats/exporters.py:279 weblate/formats/ttkit.py:1491 msgid "JSON nested structure file" -msgstr "" +msgstr "JSON фајл са угнежђеном структуром" #: weblate/formats/exporters.py:288 weblate/formats/ttkit.py:1432 msgid "Android String Resource" @@ -4276,7 +4326,7 @@ msgstr "Android String Resource" #: weblate/formats/exporters.py:309 msgid "iOS strings" -msgstr "" +msgstr "iOS стрингови" #: weblate/formats/external.py:27 msgid "Excel Open XML" @@ -4292,11 +4342,11 @@ msgstr "Геттекст ПО фајл" #: weblate/formats/ttkit.py:1216 msgid "gettext PO file (monolingual)" -msgstr "" +msgstr "gettext PO датотека (монолингвална)" #: weblate/formats/ttkit.py:1240 msgid "Qt Linguist translation file" -msgstr "" +msgstr "Qt Linguist датотека превода" #: weblate/formats/ttkit.py:1254 #, fuzzy @@ -4318,7 +4368,7 @@ msgstr "" #: weblate/formats/ttkit.py:1331 msgid "iOS strings (UTF-8)" -msgstr "" +msgstr "iOS стрингови (UTF-8)" #: weblate/formats/ttkit.py:1338 msgid "Java Properties (UTF-8)" @@ -4330,7 +4380,7 @@ msgstr "Јава својства (UTF-16)" #: weblate/formats/ttkit.py:1357 msgid "Java Properties (ISO 8859-1)" -msgstr "" +msgstr "Java Properties (ISO 8859-1)" #: weblate/formats/ttkit.py:1369 msgid "Joomla language file" @@ -4350,11 +4400,11 @@ msgstr "PHP стрингови" #: weblate/formats/ttkit.py:1413 msgid "Laravel PHP strings" -msgstr "" +msgstr "Laravel PHP низови" #: weblate/formats/ttkit.py:1420 msgid ".NET resource file" -msgstr "" +msgstr ".NET ресурсна датотека" #: weblate/formats/ttkit.py:1452 msgid "Mobile Kotlin Resource" @@ -4372,7 +4422,7 @@ msgstr "JSON фајл" #: weblate/formats/ttkit.py:1498 msgid "WebExtension JSON file" -msgstr "" +msgstr "WebExtension JSON фајл" #: weblate/formats/ttkit.py:1508 msgid "i18next JSON file v3" @@ -4432,35 +4482,35 @@ msgstr "" #: weblate/formats/ttkit.py:1706 msgid "YAML file" -msgstr "" +msgstr "YAML фајл" #: weblate/formats/ttkit.py:1725 msgid "Ruby YAML file" -msgstr "" +msgstr "Ruby YAML фајл" #: weblate/formats/ttkit.py:1733 msgid "DTD file" -msgstr "" +msgstr "DTD фајл" #: weblate/formats/ttkit.py:1776 msgid "SubRip subtitle file" -msgstr "" +msgstr "SubRip фајл титлова" #: weblate/formats/ttkit.py:1791 msgid "MicroDVD subtitle file" -msgstr "" +msgstr "MicroDVD датотека титлова" #: weblate/formats/ttkit.py:1798 msgid "Advanced SubStation Alpha subtitle file" -msgstr "" +msgstr "Напредна SubStation Alpha датотека титлова" #: weblate/formats/ttkit.py:1805 msgid "SubStation Alpha subtitle file" -msgstr "" +msgstr "SubStation Alpha датотека титлова" #: weblate/formats/ttkit.py:1812 msgid "Flat XML file" -msgstr "" +msgstr "Flat XML фајл" #: weblate/formats/ttkit.py:1821 #, fuzzy @@ -4512,7 +4562,7 @@ msgstr "Појмовници" #: weblate/glossary/forms.py:29 msgid "Invalid integer list!" -msgstr "" +msgstr "Невалидна листа целих бројева!" #: weblate/glossary/forms.py:81 weblate/templates/mail/base.html:40 #: weblate/templates/snippets/component-glossary-badge.html:4 @@ -4788,7 +4838,7 @@ msgstr "Извор дефиниције множине" #: weblate/lang/models.py:809 msgid "Default plural" -msgstr "" +msgstr "Подразумевани множина" #: weblate/lang/models.py:810 #, fuzzy @@ -4863,7 +4913,7 @@ msgstr "Уклоњен је %s језик." #: weblate/legal/forms.py:11 msgid "I agree with the Terms of Service document" -msgstr "" +msgstr "Слажем се са документом Услови коришћења" #: weblate/legal/middleware.py:50 msgid "" @@ -4883,11 +4933,11 @@ msgstr "Правне одредбе" #: weblate/legal/templates/legal/confirm.html:13 msgid "You have to agree to the Terms of Service" -msgstr "" +msgstr "Морате се сложити са Условима коришћења" #: weblate/legal/templates/legal/confirm.html:15 msgid "Please read following Terms of Service document:" -msgstr "" +msgstr "Молимо прочитајте следећи документ Услови коришћења:" #: weblate/legal/templates/legal/confirm.html:23 #: weblate/templates/contributor-agreement.html:30 @@ -4898,7 +4948,7 @@ msgstr "Пошаљи" #: weblate/legal/templates/legal/contracts.html:7 #: weblate/legal/templates/legal/privacy.html:7 weblate/legal/views.py:21 msgid "Privacy" -msgstr "" +msgstr "Приватност" #: weblate/legal/templates/legal/contracts.html:15 weblate/legal/views.py:22 msgid "Subcontractors" @@ -4910,7 +4960,7 @@ msgstr "Колачићи" #: weblate/legal/templates/legal/cookies.html:14 msgid "Cookies Policy" -msgstr "" +msgstr "Политика колачића" #: weblate/legal/templates/legal/cookies.html:18 #: weblate/legal/templates/legal/index.html:19 @@ -4918,6 +4968,9 @@ msgid "" "This page is based on the Terms of Service and the Privacy Policy, you " "should still read the original documents to fully understand them:" msgstr "" +"Ова страница је заснована на Условима коришћења и Политици приватности, ипак " +"би требало да прочитате оригиналне документе да бисте их у потпуности " +"разумели:" #: weblate/legal/templates/legal/cookies.html:22 #: weblate/legal/templates/legal/index.html:23 @@ -4935,27 +4988,33 @@ msgstr "Политика приватности" #: weblate/legal/templates/legal/cookies.html:27 msgid "Cookies are used on this site for the following:" -msgstr "" +msgstr "Cookies се користе на овом сајту за следеће:" #: weblate/legal/templates/legal/cookies.html:31 msgid "" "Authentication cookies which are required for recognizing authenticated " "users." msgstr "" +"Аутентификациони колачићи који су потребни за препознавање аутентификованих " +"корисника." #: weblate/legal/templates/legal/cookies.html:32 msgid "Preferences cookie to store user preferences in certain situations." msgstr "" +"Колачић за преференције за чување корисничких преференција у одређеним " +"ситуацијама." #: weblate/legal/templates/legal/cookies.html:34 msgid "" "This site uses Matomo to analyze website traffic. It honours \"Do Not " "Track\" settings in your browser." msgstr "" +"Овај сајт користи Matomo за анализу саобраћаја на вебсајту. Поштује " +"подешавања \"Не прати\" у вашем прегледачу." #: weblate/legal/templates/legal/cookies.html:37 msgid "This site uses Google Analytics to analyze website traffic." -msgstr "" +msgstr "Овај сајт користи Google Analytics за анализу саобраћаја на вебсајту." #: weblate/legal/templates/legal/index.html:13 msgid "Legal Terms Overview" @@ -4963,7 +5022,7 @@ msgstr "Прегед правних термина" #: weblate/legal/templates/legal/terms.html:6 msgid "Terms" -msgstr "" +msgstr "Термини" #: weblate/legal/views.py:18 weblate/templates/snippets/info.html:12 #: weblate/templates/translation.html:36 @@ -5337,7 +5396,7 @@ msgstr "Фајл" #: weblate/memory/forms.py:16 msgid "You can upload a TMX or JSON file." -msgstr "" +msgstr "Можете отпремити TMX или JSON фајл." #: weblate/memory/forms.py:22 msgid "Confirm deleting all translation memory entries" @@ -5345,7 +5404,7 @@ msgstr "Потврдите брисање свих уноса из превод #: weblate/memory/models.py:163 msgid "Unsupported file!" -msgstr "" +msgstr "Неподржани фајл!" #: weblate/memory/models.py:166 msgid "No valid entries found in the uploaded file!" @@ -5407,7 +5466,7 @@ msgstr "Превод ће бити освежен у позадини." #: weblate/memory/views.py:138 msgid "File processed, the entries will appear shortly." -msgstr "" +msgstr "Фајл је обрађен, уноси ће се појавити ускоро." #: weblate/metrics/wrapper.py:18 msgctxt "Short name of month" @@ -5529,6 +5588,7 @@ msgstr "Лош захтев" msgid "" "We’re sorry but something appears to be wrong with the request you made." msgstr "" +"Жао нам је, али изгледа да нешто није у реду са захтевом који сте направили." #: weblate/templates/403.html:5 weblate/templates/403.html:12 #: weblate/templates/403_csrf.html:5 weblate/templates/403_csrf.html:11 @@ -5762,7 +5822,7 @@ msgstr "Контактирајте нас ради комерцијалног х #: weblate/templates/about/index.html:49 msgid "Weblate is built on libre software" -msgstr "" +msgstr "Веблејт је изграђен на либре софтверу" #: weblate/templates/about/keys.html:8 weblate/trans/views/about.py:95 msgid "Weblate keys" @@ -5802,7 +5862,7 @@ msgstr "" #: weblate/templates/trans/project-access.html:107 #: weblate/templates/trans/project-access.html:328 msgid "Copy to clipboard" -msgstr "" +msgstr "Копирај у клипборд" #: weblate/templates/about/keys.html:26 #, python-format @@ -5810,10 +5870,12 @@ msgid "" "All commits made with Weblate are signed with the GPG key %(gpg_key_id)s, " "for which the corresponding public key is found below." msgstr "" +"Сви комити направљени са Веблејтом су потписани са GPG кључем %(gpg_key_id)" +"s, за који се одговарајући јавни кључ налази испод." #: weblate/templates/about/keys.html:31 msgid "Commit signing is not configured." -msgstr "" +msgstr "Потписивање комита није конфигурисано." #: weblate/templates/about/stats.html:8 #: weblate/templates/dashboard/user.html:39 weblate/templates/data.html:52 @@ -6045,7 +6107,7 @@ msgstr "" #: weblate/templates/accounts/login.html:44 msgid "Sign in with:" -msgstr "" +msgstr "Пријавите се са:" #: weblate/templates/accounts/login.html:59 msgid "Forgot your password?" @@ -6058,6 +6120,7 @@ msgstr "Региструј нови налог" #: weblate/templates/accounts/password-warning.html:3 msgid "Please enable the password authentication to secure your account." msgstr "" +"Молимо омогућите аутентификацију лозинком да бисте обезбедили свој налог." #: weblate/templates/accounts/password-warning.html:8 msgid "You will be still able to authenticate using third party services." @@ -6130,6 +6193,8 @@ msgid "" "Send a request to the project you want to translate to add a missing " "language." msgstr "" +"Пошаљите захтев пројекту који желите да преводите да додате недостајући " +"језик." #: weblate/templates/accounts/profile.html:48 #: weblate/templates/accounts/profile.html:61 @@ -6168,6 +6233,8 @@ msgid "" "Add all projects you want to translate to see them as watched projects on " "the dashboard." msgstr "" +"Додајте све пројекте које желите да преводите да бисте их видели као праћене " +"пројекте на контролној табли." #: weblate/templates/accounts/profile.html:80 #: weblate/templates/mail/base.html:110 @@ -6363,7 +6430,7 @@ msgstr "Опис" #: weblate/templates/accounts/profile.html:393 msgid "Contact us immediately if you see anything suspicious in the audit log." -msgstr "" +msgstr "Контактирајте нас одмах ако видите нешто сумњиво у дневнику ревизије." #: weblate/templates/accounts/profile.html:402 #: weblate/templates/accounts/user.html:270 @@ -6441,6 +6508,8 @@ msgid "" "The API key is also used to authenticate to automatically exported Git " "repositories." msgstr "" +"API кључ се такође користи за аутентификацију при аутоматском извозу Git " +"складишта." #: weblate/templates/accounts/profile.html:493 msgid "" @@ -6462,7 +6531,7 @@ msgstr "" #: weblate/templates/accounts/profile.html:500 msgid "You have agreed to the following as a contributor:" -msgstr "" +msgstr "Сложили сте се са следећим као доприносилац:" #: weblate/templates/accounts/profile.html:515 msgid "Licenses for individual translations" @@ -6510,7 +6579,7 @@ msgstr "Уреди поставке компоненте" #: weblate/templates/accounts/redirect.html:14 #: weblate/templates/accounts/token.html:12 msgid "Redirecting you to the authentication provider." -msgstr "" +msgstr "Преусмеравање на провајдера за аутентификацију." #: weblate/templates/accounts/register.html:20 msgid "Please complete the registration to accept the invitation." @@ -6522,7 +6591,7 @@ msgstr "Региструјте се на Веблејту" #: weblate/templates/accounts/register.html:59 msgid "Create an account using:" -msgstr "" +msgstr "Направите налог користећи:" #: weblate/templates/accounts/register.html:73 msgid "Sorry, new registrations are turned off on this site." @@ -6530,7 +6599,7 @@ msgstr "Нажалост, нове регистрације су искључе #: weblate/templates/accounts/register.html:74 msgid "You can contact us for more details." -msgstr "" +msgstr "Можете нас контактирати за више детаља." #: weblate/templates/accounts/removal.html:14 msgid "Account removal" @@ -6547,6 +6616,8 @@ msgid "" "You will have to confirm account removal by clicking link in e-mail you will " "receive." msgstr "" +"Мораћете да потврдите уклањање налога кликом на линк у е-пошти коју ћете " +"добити." #: weblate/templates/accounts/removal.html:26 msgid "Delete this account" @@ -6639,7 +6710,7 @@ msgstr "Корисници" #: weblate/templates/accounts/user.html:27 #, python-format msgid "Joined on %(date)s" -msgstr "" +msgstr "Придружио се %(date)s" #: weblate/templates/accounts/user.html:31 #, python-format @@ -6900,7 +6971,7 @@ msgstr "Затвори" #: weblate/templates/trans/project-access.html:232 #: weblate/templates/translate.html:408 msgid "Are you absolutely sure?" -msgstr "" +msgstr "Да ли сте апсолутно сигурни?" #: weblate/templates/accounts/user.html:387 #, python-format @@ -6997,7 +7068,7 @@ msgstr[2] "Инсталирано %(count)s додатака" #: weblate/templates/addons/addon_list.html:44 msgid "Uninstall" -msgstr "" +msgstr "Деинсталирати" #: weblate/templates/addons/addon_list.html:45 #, fuzzy @@ -7042,7 +7113,7 @@ msgstr "Пример додатка" #: weblate/templates/addons/addon_list.html:88 #: weblate/templates/machinery/list.html:71 msgid "Install" -msgstr "" +msgstr "Инсталирати" #: weblate/templates/addons/addon_list.html:97 msgid "" @@ -7106,7 +7177,7 @@ msgstr "Упутства за интеграцију" #: weblate/templates/addons/cdnjs.html:7 msgid "Include following snippet to load the localization from the CDN:" -msgstr "" +msgstr "Укључите следећи исечак да учитате локализацију са CDN-а:" #: weblate/templates/addons/cdnjs.html:13 msgid "The localizable elements have to match configured CSS selector." @@ -7161,6 +7232,8 @@ msgid "" "You can use Django templates markup in both filename and content, for " "example:" msgstr "" +"Можете користити Django шаблоне у оба, и у имену фајла и у садржају, на " +"пример:" #: weblate/templates/addons/generate_help.html:14 msgid "Percents translated" @@ -7181,7 +7254,7 @@ msgstr "Назив компоненте" #: weblate/templates/addons/generate_help.html:24 msgid "Component URL slug" -msgstr "" +msgstr "Слаг URL-а компоненте" #: weblate/templates/addons/generate_help.html:26 #: weblate/trans/models/project.py:136 @@ -7190,7 +7263,7 @@ msgstr "Назив пројекта" #: weblate/templates/addons/generate_help.html:28 msgid "Project URL slug" -msgstr "" +msgstr "Слаг URL-а пројекта" #: weblate/templates/addons/pseudolocale.html:4 #, fuzzy @@ -7216,13 +7289,15 @@ msgstr "Изворна ниска" #: weblate/templates/addons/squash_help.html:4 msgid "" "Weblate commits from components within this repository will be squashed." -msgstr "" +msgstr "Веблејт комитовања из компоненти унутар овог складишта ће бити спојена." #: weblate/templates/addons/squash_help.html:7 msgid "" "Original commit messages are kept, but authorship is lost unless \"Per " "author\" is selected or the commit message is customized to include it." msgstr "" +"Оригиналне поруке комитова се чувају, али се ауторство губи осим ако није " +"изабрано \"По аутору\" или је порука комита прилагођена да га укључи." #: weblate/templates/auth/team.html:12 #: weblate/templates/trans/project-access.html:11 @@ -7274,7 +7349,7 @@ msgstr "Администратори" #: weblate/templates/trans/project-access-row.html:25 #: weblate/templates/trans/project-access.html:59 msgid "Not yet signed in" -msgstr "" +msgstr "Још увек нисте пријављени" #: weblate/templates/auth/team.html:117 #, fuzzy @@ -7474,7 +7549,7 @@ msgstr "Одјава" #: weblate/templates/base.html:294 weblate/templates/base.html:297 msgid "Help" -msgstr "" +msgstr "Помоћ" #: weblate/templates/base.html:303 msgid "Get support" @@ -7512,7 +7587,7 @@ msgstr "Детаљи снимка екрана" #: weblate/templates/billing/detail.html:26 msgid "Recurring payment" -msgstr "" +msgstr "Понављајуће плаћање" #: weblate/templates/billing/detail.html:29 msgid "Expired" @@ -7520,7 +7595,7 @@ msgstr "Истекло" #: weblate/templates/billing/detail.html:31 msgid "Past due date" -msgstr "" +msgstr "Прошао датум доспећа" #: weblate/templates/billing/detail.html:33 msgid "Exceeds limits" @@ -7529,7 +7604,7 @@ msgstr "Прелази ограничења" #: weblate/templates/billing/detail.html:41 #: weblate/templates/billing/detail.html:55 msgid "To terminate billing you have to remove all projects first." -msgstr "" +msgstr "Да бисте прекинули наплату, морате прво уклонити све пројекте." #: weblate/templates/billing/detail.html:47 msgid "Change plan or payment method" @@ -7537,7 +7612,7 @@ msgstr "Измени план или начин плаћања" #: weblate/templates/billing/detail.html:51 msgid "Terminate recurring payment" -msgstr "" +msgstr "Прекини понављајуће плаћање" #: weblate/templates/billing/detail.html:58 msgid "Terminate billing plan" @@ -7628,13 +7703,15 @@ msgstr "Приказ" #: weblate/templates/billing/overview.html:15 msgid "No subscriptions or due payments." -msgstr "" +msgstr "Нема претплата или дуговања." #: weblate/templates/billing/overview.html:16 msgid "" "If you miss access to billing of your project, please ask its admins to " "grant you access." msgstr "" +"Ако вам недостаје приступ наплати вашег пројекта, молимо питајте његове " +"администраторе да вам дају приступ." #: weblate/templates/billing/overview.html:25 #: weblate/templates/projects.html:16 @@ -7713,7 +7790,7 @@ msgstr "Svima dostupno." #: weblate/templates/bootstrap3/layout/radioselect_access.html:35 #: weblate/templates/snippets/info.html:196 msgid "Any authenticated user can contribute." -msgstr "" +msgstr "Сваки аутентификовани корисник може допринети." #: weblate/templates/bootstrap3/layout/radioselect_access.html:39 msgid "VCS repository might be exposed to everybody." @@ -7743,6 +7820,8 @@ msgid "" "Only use this if you know what you are doing, enabling it might revoke your " "access to this project." msgstr "" +"Користите ово само ако знате шта радите, омогућавање овога може вам " +"ускратити приступ овом пројекту." #: weblate/templates/bootstrap3/layout/radioselect_access.html:82 #: weblate/trans/forms.py:2082 weblate/trans/forms.py:2123 @@ -7803,7 +7882,7 @@ msgstr "Фајлови" #: weblate/templates/project.html:58 weblate/templates/project.html:59 #: weblate/templates/project.html:60 msgid "Download for offline translation." -msgstr "" +msgstr "Преузмите за офлајн превод." #: weblate/templates/category-project.html:30 #: weblate/templates/component-list.html:22 @@ -7855,7 +7934,7 @@ msgstr "Прегледајте све измене корисника" #: weblate/templates/project.html:188 weblate/templates/replace.html:45 #: weblate/templates/translation.html:271 msgid "Replace" -msgstr "" +msgstr "Замени" #: weblate/templates/category-project.html:114 #: weblate/templates/category.html:164 weblate/templates/component.html:205 @@ -7863,12 +7942,12 @@ msgstr "" #: weblate/templates/project.html:207 weblate/templates/translation.html:251 #: weblate/templates/translation.html:290 msgid "Apply" -msgstr "" +msgstr "Примени" #: weblate/templates/category.html:39 #: weblate/templates/language-project.html:32 weblate/templates/project.html:43 msgid "Announcements" -msgstr "" +msgstr "Обавештења" #: weblate/templates/category.html:42 weblate/templates/component.html:48 #: weblate/templates/dashboard/user.html:40 weblate/templates/project.html:48 @@ -7961,6 +8040,8 @@ msgid "" "The message is shown for all translations within the project, until its " "given expiry, or permanently until it is deleted." msgstr "" +"Порука се приказује за све преводе у оквиру пројекта, до истека који је дат, " +"или трајно док не буде обрисана." #: weblate/templates/category.html:248 weblate/templates/component.html:280 #: weblate/templates/dashboard/user.html:102 weblate/templates/project.html:298 @@ -7968,6 +8049,8 @@ msgid "" "You don't have permission to view reports for all users, only your " "contributions will be listed." msgstr "" +"Немате дозволу да видите извештаје за све кориснике, само ће ваши доприноси " +"бити наведени." #: weblate/templates/category.html:258 weblate/templates/component.html:290 #: weblate/templates/dashboard/user.html:112 weblate/templates/project.html:308 @@ -7980,6 +8063,9 @@ msgid "" "Useful for inclusion in documentation or the app itself, to thank " "translators and generate feedback to them." msgstr "" +"Листа све преводиоце који доприносе овом пројекту у датом временском " +"периоду. Корисно за укључивање у документацију или саму апликацију, да се " +"захвалите преводиоцима и генеришете повратне информације за њих." #: weblate/templates/category.html:266 weblate/templates/category.html:285 #: weblate/templates/component.html:298 weblate/templates/component.html:317 @@ -7997,7 +8083,7 @@ msgstr "Статистика сарадника" #: weblate/templates/category.html:281 weblate/templates/component.html:313 #: weblate/templates/dashboard/user.html:135 weblate/templates/project.html:331 msgid "Reports the number of strings and words translated by each translator." -msgstr "" +msgstr "Извештава о броју стрингова и речи које је превео сваки преводилац." #: weblate/templates/check_list.html:27 msgid "Total" @@ -8005,7 +8091,7 @@ msgstr "Укупно" #: weblate/templates/check_list.html:28 msgid "Dismissed" -msgstr "" +msgstr "Одбачено" #: weblate/templates/check_list.html:30 weblate/templates/engage.html:33 #: weblate/templates/snippets/info.html:489 @@ -8028,7 +8114,7 @@ msgstr "Информације" #: weblate/templates/component-progress.html:9 #: weblate/templates/multi-progress.html:9 msgid "Progress" -msgstr "" +msgstr "Напредак" #: weblate/templates/component-progress.html:17 msgid "Component is being updated…" @@ -8043,7 +8129,7 @@ msgstr "Позивница на %(site_title)s" #: weblate/templates/component-progress.html:33 msgid "Abort the update" -msgstr "" +msgstr "Прекини ажурирање" #: weblate/templates/component-progress.html:38 #: weblate/templates/multi-progress.html:30 @@ -8054,7 +8140,7 @@ msgstr "Врати се на компоненту након завршавањ #: weblate/templates/component.html:36 weblate/wladmin/views.py:80 msgid "Alerts" -msgstr "" +msgstr "Упозорења" #: weblate/templates/component.html:45 weblate/templates/project.html:45 msgid "Download statistics (CSV)" @@ -8062,7 +8148,7 @@ msgstr "Преузми статистику (CSV)" #: weblate/templates/component.html:46 weblate/templates/project.html:46 msgid "Download statistics (JSON)" -msgstr "" +msgstr "Статистика преузимања (JSON)" #: weblate/templates/component.html:74 weblate/templates/data.html:8 #: weblate/templates/project.html:74 weblate/templates/translation.html:75 @@ -8088,7 +8174,7 @@ msgstr "Снимци екрана" #: weblate/templates/component.html:101 weblate/templates/guide.html:9 #: weblate/templates/guide.html:25 msgid "Community localization checklist" -msgstr "" +msgstr "Контролна листа за локализацију заједнице" #: weblate/templates/component.html:137 weblate/templates/manage/alerts.html:22 msgid "dismissed" @@ -8098,13 +8184,13 @@ msgstr "одбијено" #: weblate/templates/component.html:144 #, python-format msgid "Appeared %(first)s, last seen %(last)s" -msgstr "" +msgstr "Појавио се %(first)s, последњи пут виђен %(last)s" #: weblate/templates/component.html:148 #: weblate/templates/manage/performance.html:44 #: weblate/templates/translate.html:507 msgid "Dismiss" -msgstr "" +msgstr "Одбацити" #: weblate/templates/component.html:156 msgid "Please sign in to see the alerts." @@ -8119,6 +8205,8 @@ msgid "" "The message is shown for all translations within the component, until its " "given expiry, or permanently until it is deleted." msgstr "" +"Порука се приказује за све преводе унутар компоненте, до датог истека, или " +"трајно док се не обрише." #: weblate/templates/component.html:245 #, fuzzy @@ -8141,6 +8229,9 @@ msgid "" "Useful for inclusion in documentation or the app itself, to thank " "translators and generate feedback to them." msgstr "" +"Листа свих преводилаца који доприносе овој компоненти у датом временском " +"периоду. Корисно за укључивање у документацију или саму апликацију, да се " +"захвалите преводиоцима и генеришете повратне информације за њих." #: weblate/templates/contributor-agreement.html:8 #: weblate/templates/snippets/info.html:164 @@ -8152,11 +8243,11 @@ msgstr "Споразум о доприносу" #: weblate/templates/contributor-agreement.html:15 msgid "Please review the contributor agreement" -msgstr "" +msgstr "Молимо прегледајте споразум о доприносу" #: weblate/templates/contributor-agreement.html:17 msgid "Please read following contributor agreement:" -msgstr "" +msgstr "Молимо прочитајте следећи споразум о доприносу:" #: weblate/templates/contributor-agreement.html:28 msgid "You have already agreed to this agreement." @@ -8200,12 +8291,16 @@ msgid "" "Choose what languages you want in the preferences, to see overview of " "available translations for those languages in your watched projects." msgstr "" +"Изаберите које језике желите у преференцијама, да бисте видели преглед " +"доступних превода за те језике у вашим праћеним пројектима." #: weblate/templates/dashboard/user.html:82 msgid "" "Could not find any suggestions for you, please choose your languages in the " "preferences to get some." msgstr "" +"Нисмо могли пронаћи никакве предлоге за вас, молимо изаберите своје језике у " +"преференцијама да бисте добили неке." #: weblate/templates/dashboard/user.html:116 msgid "" @@ -8213,6 +8308,9 @@ msgid "" "inclusion in documentation or the app itself, to thank translators and " "generate feedback to them." msgstr "" +"Листа свих преводилаца који су допринели у датом временском периоду. Корисно " +"за укључивање у документацију или саму апликацију, како би се захвалили " +"преводиоцима и генерисали повратне информације за њих." #: weblate/templates/data.html:14 msgid "Various data from Weblate is available in machine-readable format." @@ -8275,6 +8373,8 @@ msgstr "Измене из удаљеног складишта до Веблеј msgid "" "Weblate also supports direct notifications from several code hosting sites:" msgstr "" +"Веблејт такође подржава директна обавештења са неколико сајтова за хостовање " +"кода:" #: weblate/templates/data.html:138 msgid "Hosting site" @@ -8382,7 +8482,7 @@ msgstr[2] "Преводи се на %(count)s језика." #, python-format msgid "" "Overall, these translations are %(percent)s%% complete." -msgstr "" +msgstr "Укупно, ови преводи су %(percent)s%% комплетни." #: weblate/templates/engage.html:51 #, python-format @@ -8425,7 +8525,7 @@ msgstr "Прикажи језике пројекта" #: weblate/templates/fonts/font_list.html:9 #: weblate/templates/fonts/font_list.html:16 weblate/templates/project.html:108 msgid "Fonts" -msgstr "" +msgstr "Фонтови" #: weblate/templates/fonts/font_detail.html:20 msgid "File size" @@ -8448,7 +8548,7 @@ msgstr "Користи се у групама" #: weblate/templates/fonts/font_list.html:15 #: weblate/templates/fonts/fontgroup_detail.html:9 msgid "Font groups" -msgstr "" +msgstr "Групе фонта" #: weblate/templates/fonts/font_list.html:25 msgid "Group name" @@ -8460,11 +8560,12 @@ msgstr "Заменски језици" #: weblate/templates/fonts/font_list.html:51 msgid "Add font group" -msgstr "" +msgstr "Додајте групу фонта" #: weblate/templates/fonts/font_list.html:62 msgid "Please upload fonts to be able to define font groups." msgstr "" +"Молимо вас да отпремите фонтове да бисте могли да дефинишете групе фонта." #: weblate/templates/fonts/font_list.html:92 msgid "Add font" @@ -8480,7 +8581,7 @@ msgstr "Отпреми" #: weblate/templates/fonts/fontgroup_detail.html:16 msgid "Font group" -msgstr "" +msgstr "Група фонта" #: weblate/templates/fonts/fontgroup_detail.html:22 msgid "language override" @@ -8492,7 +8593,7 @@ msgstr "Додај заменски језик" #: weblate/templates/fonts/fontgroup_detail.html:60 msgid "Edit font group" -msgstr "" +msgstr "Уредите групу фонта" #: weblate/templates/footer.html:5 #, python-format @@ -8514,6 +8615,8 @@ msgid "" "Here you can find guidance to make your localization project attractive to " "the community." msgstr "" +"Овде можете пронаћи смернице како да ваш пројекат локализације учините " +"атрактивним за заједницу." #: weblate/templates/guide.html:64 msgid "Return to the component" @@ -8521,112 +8624,114 @@ msgstr "Врати се на компоненту" #: weblate/templates/i18n.html:23 msgid "Use a few words, avoid common phrases." -msgstr "" +msgstr "Користите неколико речи, избегавајте уобичајене фразе." #: weblate/templates/i18n.html:24 msgid "No need for symbols, digits, or uppercase letters." -msgstr "" +msgstr "Нема потребе за симболима, цифрама или великим словима." #: weblate/templates/i18n.html:25 msgid "Add another word or two. Uncommon words are better." -msgstr "" +msgstr "Додајте још једну или две речи. Неуобичајене речи су боље." #: weblate/templates/i18n.html:26 msgid "Straight rows of keys are easy to guess." -msgstr "" +msgstr "Праве редове тастера је лако погодити." #: weblate/templates/i18n.html:27 msgid "Short keyboard patterns are easy to guess." -msgstr "" +msgstr "Кратке шеме тастатуре је лако погодити." #: weblate/templates/i18n.html:28 msgid "Use a longer keyboard pattern with more turns." -msgstr "" +msgstr "Користите дужу шему тастатуре са више скретања." #: weblate/templates/i18n.html:29 msgid "Repeats like \"aaa\" are easy to guess." -msgstr "" +msgstr "Понављања као што је \"ааа\" је лако погодити." #: weblate/templates/i18n.html:30 msgid "" "Repeats like \"abcabcabc\" are only slightly harder to guess than \"abc\"." msgstr "" +"Понављања као што је \"абцабцабц\" су само мало тежа за погодити од \"абц\"." #: weblate/templates/i18n.html:31 msgid "Avoid repeated words and characters." -msgstr "" +msgstr "Избегавајте понављајуће речи и карактере." #: weblate/templates/i18n.html:32 msgid "Sequences like \"abc\" or \"6543\" are easy to guess." -msgstr "" +msgstr "Секвенце као што су \"абц\" или \"6543\" је лако погодити." #: weblate/templates/i18n.html:33 msgid "Avoid sequences." -msgstr "" +msgstr "Избегавајте секвенце." #: weblate/templates/i18n.html:34 msgid "Recent years are easy to guess." -msgstr "" +msgstr "Последње године је лако погодити." #: weblate/templates/i18n.html:35 msgid "Avoid recent years." -msgstr "" +msgstr "Избегавајте последње године." #: weblate/templates/i18n.html:36 msgid "Avoid years that are associated with you." -msgstr "" +msgstr "Избегавајте године које су повезане са вама." #: weblate/templates/i18n.html:37 msgid "Dates are often easy to guess." -msgstr "" +msgstr "Датуми су често лако погодити." #: weblate/templates/i18n.html:38 msgid "Avoid dates and years that are associated with you." -msgstr "" +msgstr "Избегавајте датуме и године које су повезане са вама." #: weblate/templates/i18n.html:39 msgid "This is a top-10 common password." -msgstr "" +msgstr "Ово је једна од 10 најчешћих лозинки." #: weblate/templates/i18n.html:40 msgid "This is a top-100 common password." -msgstr "" +msgstr "Ово је једна од 100 најчешћих лозинки." #: weblate/templates/i18n.html:41 msgid "This is a very common password." -msgstr "" +msgstr "Ово је веома честа лозинка." #: weblate/templates/i18n.html:42 msgid "This is similar to a commonly used password." -msgstr "" +msgstr "Ово је слично често коришћеној лозинки." #: weblate/templates/i18n.html:43 msgid "A word by itself is easy to guess." -msgstr "" +msgstr "Реч сама по себи је лако погодити." #: weblate/templates/i18n.html:44 msgid "Names and surnames by themselves are easy to guess." -msgstr "" +msgstr "Имена и презимена сама по себи је лако погодити." #: weblate/templates/i18n.html:45 msgid "Common names and surnames are easy to guess." -msgstr "" +msgstr "Уобичајена имена и презимена је лако погодити." #: weblate/templates/i18n.html:46 msgid "Capitalization doesn't help very much." -msgstr "" +msgstr "Велика слова не помажу много." #: weblate/templates/i18n.html:47 msgid "All-uppercase is almost as easy to guess as all-lowercase." msgstr "" +"Све великим словима је скоро једнако лако погодити као и све малим словима." #: weblate/templates/i18n.html:48 msgid "Reversed words aren't much harder to guess." -msgstr "" +msgstr "Обрнуте речи нису много теже за погодити." #: weblate/templates/i18n.html:49 msgid "Predictable substitutions like '@' instead of 'a' don't help very much." -msgstr "" +msgstr "Предвидљиве замене као што је '@' уместо 'а' не помажу много." #: weblate/templates/i18n.html:50 msgid "Your password can't contain repeated words or characters." @@ -8779,7 +8884,7 @@ msgstr "" #: weblate/templates/js/git-status.html:123 msgid "Cleanup" -msgstr "" +msgstr "Чишћење" #: weblate/templates/js/git-status.html:125 msgid "Cleanup all untracked files in the Weblate repository" @@ -8872,7 +8977,7 @@ msgstr "Додај језик" #: weblate/templates/lang/language_form.html:16 weblate/trans/forms.py:2154 msgid "Workflow" -msgstr "" +msgstr "Радни ток" #: weblate/templates/lang/plural_form.html:15 msgid "Changing plural rules might not have a desired effect." @@ -8943,12 +9048,12 @@ msgstr "Коментар превода" #: weblate/templates/list-comments.html:29 msgid "Resolve" -msgstr "" +msgstr "Реши" #: weblate/templates/list-comments.html:37 #: weblate/templates/snippets/suggestions.html:42 msgid "Mark as spam" -msgstr "" +msgstr "Означи као спам" #: weblate/templates/machinery/detail.html:7 msgid "This service is installed site wide." @@ -9038,7 +9143,7 @@ msgstr "Има промена у вашем налогу на %(site_title)s:" #: weblate/templates/mail/account_activity_subject.txt:4 #, python-format msgid "Activity on your account at %(site_title)s" -msgstr "" +msgstr "Активност на вашем налогу на %(site_title)s" #: weblate/templates/mail/activation.html:7 #, python-format @@ -9046,6 +9151,8 @@ msgid "" "This is an automatic e-mail to help you complete your registration with " "%(site_title)s." msgstr "" +"Ово је аутоматска е-пошта која вам помаже да завршите регистрацију на " +"%(site_title)s." #: weblate/templates/mail/activation_subject.txt:4 #, python-format @@ -9125,7 +9232,7 @@ msgstr "" #: weblate/templates/mail/billing_expired.html:40 msgid "All projects covered by this billing plan have been removed." -msgstr "" +msgstr "Сви пројекти покривени овим планом наплате су уклоњени." #: weblate/templates/mail/billing_expired.html:44 #, python-format @@ -9147,7 +9254,7 @@ msgstr "Ваш пројекат превођења је уклоњен" #: weblate/templates/mail/billing_expired_subject.txt:5 msgid "Your translation project is scheduled for removal" -msgstr "" +msgstr "Ваш пројекат превода је заказан за уклањање" #: weblate/templates/mail/billing_expired_subject.txt:7 msgid "Your trial period is about to expire" @@ -9155,7 +9262,7 @@ msgstr "" #: weblate/templates/mail/billing_expired_subject.txt:9 msgid "Your billing plan has expired" -msgstr "" +msgstr "Ваш план наплате је истекао" #: weblate/templates/mail/changed_translation.html:14 #: weblate/templates/mail/new_suggestion.html:14 @@ -9218,12 +9325,12 @@ msgstr "Компонента %(component)s је закључана" #: weblate/templates/mail/component_lock_subject.txt:6 #, python-format msgid "Component %(component)s was unlocked" -msgstr "" +msgstr "Компонента %(component)s је откључана" #: weblate/templates/mail/digest_subject.txt:3 #, python-format msgid "Digest: %(notification_name)s" -msgstr "" +msgstr "Сажетак: %(notification_name)s" #: weblate/templates/mail/invite.html:12 #, fuzzy @@ -9313,6 +9420,8 @@ msgid "" "Please add the language file to VCS, Weblate will automatically discover it " "upon next update." msgstr "" +"Молимо додајте језички фајл у VCS, Веблејт ће га аутоматски открити при " +"следећем ажурирању." #: weblate/templates/mail/new_language.html:21 msgid "" @@ -9401,7 +9510,7 @@ msgstr "Грешка складишта у %(component)s" #: weblate/templates/mail/repository_operation_subject.txt:3 #, python-format msgid "Repository operation in %(component)s" -msgstr "" +msgstr "Операција складишта у %(component)s" #: weblate/templates/mail/reset-nonexisting.html:7 #, python-format @@ -9457,6 +9566,9 @@ msgid "" "might need to copy the URL on next line into the browser address bar to " "proceed." msgstr "" +"Молимо вас да кликнете на дугме за потврду испод. У случају да не ради, " +"можда ћете морати да копирате УРЛ на следећој линији у адресну траку " +"прегледача да бисте наставили." #: weblate/templates/mail/shared-registration.html:16 msgid "Confirm password reset" @@ -9540,7 +9652,7 @@ msgstr "Пројекти без компоненти" #: weblate/templates/manage/alerts.html:45 #: weblate/templates/manage/performance.html:92 msgid "Congratulations, your setup seems to work." -msgstr "" +msgstr "Честитамо, ваша конфигурација изгледа да ради." #: weblate/templates/manage/appearance.html:10 weblate/wladmin/views.py:84 msgid "Appearance" @@ -9562,23 +9674,23 @@ msgstr "Резервне копије" #: weblate/templates/manage/backups.html:20 #, python-format msgid "Backup service: %(url)s" -msgstr "" +msgstr "Услуга резервне копије: %(url)s" #: weblate/templates/manage/backups.html:22 msgid "Turned off" -msgstr "" +msgstr "Искључено" #: weblate/templates/manage/backups.html:33 msgid "Backup service credentials" -msgstr "" +msgstr "Акредитиви услуге резервне копије" #: weblate/templates/manage/backups.html:62 msgid "Turn off" -msgstr "" +msgstr "Искључи" #: weblate/templates/manage/backups.html:62 msgid "Turn on" -msgstr "" +msgstr "Укључи" #: weblate/templates/manage/backups.html:63 msgid "Perform backup" @@ -9589,16 +9701,20 @@ msgid "" "By removing the backup service, the credentials will be removed and you " "might lose access to the backups." msgstr "" +"Уклањањем услуге резервне копије, акредитиви ће бити уклоњени и можда ћете " +"изгубити приступ резервним копијама." #: weblate/templates/manage/backups.html:82 msgid "" "In case you want to be able to restore the backups, please record " "credentials below." msgstr "" +"У случају да желите да будете у могућности да вратите резервне копије, " +"молимо забележите акредитиве испод." #: weblate/templates/manage/backups.html:103 msgid "Add backup service" -msgstr "" +msgstr "Додај услугу резервне копије" #: weblate/templates/manage/billing.html:15 msgid "Commercial" @@ -9677,12 +9793,12 @@ msgstr "Откривање компоненти" #: weblate/templates/manage/index.html:92 msgid "Manage support package" -msgstr "" +msgstr "Управљајте пакетом подршке" #: weblate/templates/manage/index.html:95 #: weblate/templates/manage/snippets/activation-form.html:18 msgid "Purchase support package" -msgstr "" +msgstr "Купите пакет подршке" #: weblate/templates/manage/performance.html:9 #: weblate/wladmin/templates/admin/weblate-index.html:36 @@ -9696,13 +9812,16 @@ msgstr "Последње појављивање" #: weblate/templates/manage/performance.html:45 msgid "Ignore permanently" -msgstr "" +msgstr "Игнориши трајно" #: weblate/templates/manage/performance.html:54 msgid "" "You can safely permanently ignore errors for features you do not intend to " "use. These are usually caused by missing or outdated dependencies." msgstr "" +"Можете безбедно трајно игнорисати грешке за функције које не намеравате да " +"користите. Ове грешке су обично узроковане недостајућим или застарелим " +"зависностима." #: weblate/templates/manage/performance.html:65 msgid "System checks" @@ -9710,7 +9829,7 @@ msgstr "Системске провере" #: weblate/templates/manage/performance.html:102 msgid "Celery queues" -msgstr "" +msgstr "Celery редови" #: weblate/templates/manage/performance.html:123 #, fuzzy @@ -9787,13 +9906,15 @@ msgstr "Ово складиште је везано за:" #: weblate/templates/manage/snippets/activation-form.html:10 msgid "Activate support package" -msgstr "" +msgstr "Активирајте пакет подршке" #: weblate/templates/manage/snippets/activation-form.html:13 msgid "" "The support packages include priority e-mail support, or cloud backups of " "your Weblate installation." msgstr "" +"Пакети подршке укључују приоритетну е-пошту подршке, или резервне копије у " +"облаку ваше Веблејт инсталације." #: weblate/templates/manage/snippets/activation-form.html:17 msgid "Activate" @@ -9805,13 +9926,15 @@ msgstr "Резерва ризнице" #: weblate/templates/manage/snippets/backup-credentials.html:9 msgid "Passphrase" -msgstr "" +msgstr "Лозинка" #: weblate/templates/manage/snippets/backup-credentials.html:12 msgid "" "The passphrase is used to encrypt the backups and is necessary to restore " "them." msgstr "" +"Лозинка се користи за шифровање резервних копија и неопходна је за њихово " +"враћање." #: weblate/templates/manage/snippets/backup-credentials.html:14 msgid "SSH key" @@ -9824,6 +9947,7 @@ msgstr "Преузми лични кључ" #: weblate/templates/manage/snippets/backup-credentials.html:16 msgid "The private key is needed to access the remote backup repository." msgstr "" +"Приватни кључ је потребан за приступ удаљеном складишту резервних копија." #: weblate/templates/manage/ssh.html:8 #: weblate/wladmin/templates/admin/weblate-index.html:26 @@ -9910,13 +10034,15 @@ msgstr "Направи нови појмовник" #: weblate/templates/manage/tools.html:35 #: weblate/templates/manage/tools.html:42 msgid "Django admin interface" -msgstr "" +msgstr "Django администраторски интерфејс" #: weblate/templates/manage/tools.html:39 msgid "" "The Django admin interface might be useful to perform certain management " "operations." msgstr "" +"Django администраторски интерфејс може бити користан за извршавање одређених " +"управљачких операција." #: weblate/templates/manage/tools.html:49 msgid "Send test e-mail" @@ -9928,7 +10054,7 @@ msgstr "Испробај „Sentry“ интеграцију" #: weblate/templates/manage/tools.html:66 msgid "Generate a test event to test Sentry integration." -msgstr "" +msgstr "Генеришите тест догађај да тестирате Sentry интеграцију." #: weblate/templates/manage/users.html:29 #, fuzzy @@ -9977,7 +10103,7 @@ msgstr "Приказани језици" #: weblate/templates/matrix.html:42 msgid "Choose" -msgstr "" +msgstr "Изабери" #: weblate/templates/memory/index.html:28 msgid "Translation memory status" @@ -10056,7 +10182,7 @@ msgstr "укључено" #. Translators: Shared translation memory is disabled #: weblate/templates/memory/index.html:183 msgid "off" -msgstr "" +msgstr "искључено" #: weblate/templates/memory/index.html:197 #, fuzzy @@ -10173,24 +10299,24 @@ msgstr "Иди на позицију" #: weblate/templates/paginator.html:21 #: weblate/templates/snippets/position-field.html:24 msgid "Jump to" -msgstr "" +msgstr "Прескочи на" #. Translators: This is partial position indicator shown when editing position #: weblate/templates/paginator.html:24 #, python-format msgid "/ %(total)s" -msgstr "" +msgstr "/ %(total)s" #: weblate/templates/project-label-edit.html:9 #: weblate/templates/project-labels.html:10 weblate/templates/project.html:109 #: weblate/templates/translate.html:653 weblate/trans/models/unit.py:410 #: weblate/utils/views.py:179 msgid "Labels" -msgstr "" +msgstr "Ознаке" #: weblate/templates/project-label-edit.html:16 msgid "Edit label" -msgstr "" +msgstr "Urediti ознаку" #: weblate/templates/project-labels.html:18 weblate/trans/models/label.py:19 msgid "Label name" @@ -10198,7 +10324,7 @@ msgstr "Назив ознаке" #: weblate/templates/project-labels.html:19 weblate/trans/models/label.py:21 msgid "Color" -msgstr "" +msgstr "Боја" #: weblate/templates/project-labels.html:21 #, fuzzy @@ -10233,7 +10359,7 @@ msgstr "Питајте за хостинг пројекта" msgid "" "Your request was rejected because you have performed too many operations " "recently." -msgstr "" +msgstr "Ваш захтев је одбијен јер сте недавно извршили превише операција." #: weblate/templates/ratelimit.html:7 msgid "You have been signed out, please sign in and try again later." @@ -10257,7 +10383,7 @@ msgstr "Прикажи на контролној табли" #: weblate/templates/replace.html:13 msgid "Please review and confirm the search and replace results." -msgstr "" +msgstr "Молимо прегледајте и потврдите резултате претраге и замене." #: weblate/templates/replace.html:17 msgid "" @@ -10273,6 +10399,8 @@ msgstr "Придружене изворне ниске" msgid "" "Screenshot is shown to add visual context for all listed source strings." msgstr "" +"Снимак екрана је приказан да дода визуелни контекст за све наведене изворне " +"ниске." #: weblate/templates/screenshots/screenshot_detail.html:50 msgid "Assign source strings" @@ -10288,7 +10416,7 @@ msgstr "Претрага изворних ниски" #: weblate/templates/screenshots/screenshot_detail.html:110 msgid "Deleting screenshot will remove it from all associated source strings." -msgstr "" +msgstr "Брисањем снимка екрана уклонићете га из свих повезаних изворних ниски." #: weblate/templates/screenshots/screenshot_list.html:31 #: weblate/templates/translate.html:815 @@ -10394,6 +10522,7 @@ msgstr "" #: weblate/templates/snippets/bulk-help.html:3 msgid "You can change the state of all strings matching given type at once." msgstr "" +"Можете променити стање свих стрингова који одговарају датом типу одједном." #: weblate/templates/snippets/component/contributor-agreement.html:8 msgid "View contributor agreement" @@ -10401,13 +10530,15 @@ msgstr "Погледај споразум за доприносиоце" #: weblate/templates/snippets/component/lock.html:14 msgid "Get notified when this project is unlocked again" -msgstr "" +msgstr "Будите обавештени када овај пројекат поново буде откључан" #: weblate/templates/snippets/component/lock.html:17 msgid "" "The translation is temporarily closed for contributions due to maintenance, " "please come back later." msgstr "" +"Превод је привремено затворен за доприносе због одржавања, молимо вратите се " +"касније." #: weblate/templates/snippets/component/lock.html:21 msgid "The translation was automatically locked due to following alerts:" @@ -10510,7 +10641,7 @@ msgstr "Грана складишта" #: weblate/templates/snippets/git-info.html:36 msgid "Last remote commit" -msgstr "" +msgstr "Последњи удаљени комит" #. Translators: For example: "nijel authored five days ago" #: weblate/templates/snippets/git-info.html:43 @@ -10593,7 +10724,7 @@ msgstr "Лиценца превода" #: weblate/templates/snippets/info.html:142 msgid "Automatic component list assignment" -msgstr "" +msgstr "Аутоматско додељивање листе компоненти" #: weblate/templates/snippets/info.html:175 msgid "Translation process" @@ -10609,9 +10740,11 @@ msgid "Suggestions with one vote are automatically accepted as translations." msgid_plural "" "Suggestions are automatically accepted as translations once they have " "%(count)s votes." -msgstr[0] "" +msgstr[0] "Сугестије са %(count)s гласом се аутоматски прихватају као преводи." msgstr[1] "" +"Сугестије се аутоматски прихватају као преводи када имају %(count)s гласа." msgstr[2] "" +"Сугестије се аутоматски прихватају као преводи када имају %(count)s гласова." #: weblate/templates/snippets/info.html:183 msgid "Translations can be made directly." @@ -10829,10 +10962,12 @@ msgid "" "Additional labels can be defined in the project " "settings." msgstr "" +"Додатне ознаке могу бити дефинисане у подешавањима " +"пројекта." #: weblate/templates/snippets/labels_description.html:9 msgid "Additional labels can be defined by project administrators." -msgstr "" +msgstr "Додатне ознаке могу бити дефинисане од стране администратора пројекта." #: weblate/templates/snippets/libre-basic.html:4 msgid "" @@ -10995,7 +11130,7 @@ msgstr "" #: weblate/templates/snippets/list-objects.html:280 msgid "Nothing to list here." -msgstr "" +msgstr "Нема ничега за листање овде." #: weblate/templates/snippets/list-objects.html:290 msgid "Please sign in to start new translation" @@ -11007,7 +11142,7 @@ msgstr "Континуирана локализација за #: weblate/templates/snippets/login-info.html:9 msgid "By registering and signing in to this service you accept our:" -msgstr "" +msgstr "Регистрацијом и пријавом на ову услугу прихватате наше:" #: weblate/templates/snippets/login-info.html:20 msgid "" @@ -11043,6 +11178,8 @@ msgid "" "This formula identifies which plural form will be used based on given count " "(n)." msgstr "" +"Ова формула идентификује који ће се облик множине користити на основу датог " +"броја (n)." #: weblate/templates/snippets/position-field.html:8 #: weblate/templates/snippets/position-field.html:11 @@ -11052,7 +11189,7 @@ msgstr "Прва" #: weblate/templates/snippets/position-field.html:17 #, python-format msgid "%(filter_pos)s / %(filter_count)s" -msgstr "" +msgstr "%(filter_pos)s / %(filter_count)s" #: weblate/templates/snippets/position-field.html:19 #, python-format @@ -11065,7 +11202,7 @@ msgstr[2] "%(count)s ниски" #: weblate/templates/snippets/position-field.html:25 #, python-format msgid "/ %(filter_count)s" -msgstr "" +msgstr "/ %(filter_count)s" #: weblate/templates/snippets/position-field.html:31 #: weblate/templates/snippets/position-field.html:34 @@ -11089,6 +11226,8 @@ msgid "" "Version control or files to translate are configured on individual " "components:" msgstr "" +"Контрола верзија или фајлови за превођење су конфигурисани на појединачним " +"компонентама:" #: weblate/templates/snippets/project-workflow-settings.html:4 msgid "Translation workflow can be also customized per language:" @@ -11113,7 +11252,7 @@ msgstr "Овај пројекат је у пробном периоду." #: weblate/templates/snippets/query-builder.html:6 msgid "Advanced query builder" -msgstr "" +msgstr "Напредни градитељ упита" #: weblate/templates/snippets/query-builder.html:19 msgid "Target strings" @@ -11150,7 +11289,7 @@ msgstr "Тражи…" #: weblate/templates/snippets/query-builder.html:31 msgctxt "Exact search toggle" msgid "Exact" -msgstr "" +msgstr "Тачно" #: weblate/templates/snippets/query-builder.html:57 #: weblate/templates/snippets/query-builder.html:59 @@ -11159,11 +11298,11 @@ msgstr "Ниска измењена након" #: weblate/templates/snippets/query-builder.html:60 msgid "String changed before" -msgstr "Ниске измењене после" +msgstr "Ниске измењене пре" #: weblate/templates/snippets/query-builder.html:63 msgid "Date" -msgstr "" +msgstr "Датум" #: weblate/templates/snippets/query-builder.html:73 msgid "Query examples" @@ -11199,7 +11338,7 @@ msgstr "Одобрене ниске са предлозима" #: weblate/templates/snippets/query-builder.html:106 msgid "All untranslated strings added the past month" -msgstr "" +msgstr "Све непреведене ниске додате у последњем месецу" #: weblate/templates/snippets/query-builder.html:111 msgid "Filter changed strings 2 weeks ago" @@ -11277,7 +11416,7 @@ msgstr "Виџети статуса" #: weblate/templates/snippets/user-sort-field.html:9 #: weblate/templates/snippets/user-sort-field.html:13 msgid "Sort By" -msgstr "" +msgstr "Сортирај по" #: weblate/templates/snippets/sort-field.html:24 #, fuzzy @@ -11302,6 +11441,8 @@ msgid "" "Weblate uses SSH key to access remote repositories. The corresponding public " "key is found below, you can use it to grant Weblate access to a repository." msgstr "" +"Веблејт користи SSH кључ за приступ удаљеним складиштима. Одговарајући јавни " +"кључ се налази испод, можете га користити да дате Веблејту приступ складишту." #: weblate/templates/snippets/ssh-key.html:24 #, fuzzy, python-format @@ -11383,7 +11524,7 @@ msgstr "Грешке при провери:" #: weblate/templates/snippets/unit-readonly-badge.html:4 msgid "Source in review" -msgstr "" +msgstr "Извор у прегледу" #: weblate/templates/snippets/unit-readonly-badge.html:6 #, fuzzy @@ -11462,7 +11603,7 @@ msgstr "Команда" #: weblate/templates/trans/alert/cdnaddonerror.html:10 #: weblate/templates/trans/alert/parseerror.html:10 msgid "Error" -msgstr "" +msgstr "Грешка" #: weblate/templates/trans/alert/ambiguouslanguage.html:5 #, fuzzy @@ -11495,7 +11636,7 @@ msgstr "Ова компонента није исправно подешена." #: weblate/templates/trans/alert/brokenbrowserurl.html:4 msgid "The repository browser URL points to non-existing locations:" -msgstr "" +msgstr "URL прегледача складишта указује на непостојеће локације:" #: weblate/templates/trans/alert/brokenbrowserurl.html:10 msgid "" @@ -11508,6 +11649,8 @@ msgid "" "This can happen if the path within the translation files contains references " "to a parent directory, or is absolute." msgstr "" +"Ово се може десити ако путања унутар преводних фајлова садржи референце на " +"родитељски директоријум, или је апсолутна." #: weblate/templates/trans/alert/brokenprojecturl.html:2 msgid "This project is set up incorrectly." @@ -11515,7 +11658,7 @@ msgstr "Пројекат је неисправно постављен." #: weblate/templates/trans/alert/brokenprojecturl.html:4 msgid "The project website URL points to a non-existing location:" -msgstr "" +msgstr "URL вебсајта пројекта указује на непостојећу локацију:" #: weblate/templates/trans/alert/cdnaddonerror.html:4 msgid "Weblate CDN add-on could not parse the HTML files." @@ -11632,6 +11775,8 @@ msgid "" "The component contains several translation files mapped to a single language " "in Weblate." msgstr "" +"Компонента садржи неколико преводних фајлова мапираних на један језик у " +"Веблејту." #: weblate/templates/trans/alert/duplicatelanguage.html:25 msgid "Please fix this by removing one of the translation files." @@ -11642,6 +11787,8 @@ msgid "" "Avoid having translation files for both the plain language code and its " "equivalent territory designation (for example de and de_DE)." msgstr "" +"Избегавајте да имате преводне фајлове за оба, обичан језички код и његову " +"еквивалентну територијалну ознаку (на пример de и de_DE)." #: weblate/templates/trans/alert/duplicatelanguage.html:41 msgid "Language codes" @@ -11655,7 +11802,7 @@ msgstr "Назив фајла" #: weblate/templates/trans/alert/duplicatestring.html:4 msgid "The component contains several duplicated translation strings." -msgstr "" +msgstr "Компонента садржи неколико дуплираних преводних низова." #: weblate/templates/trans/alert/duplicatestring.html:10 #: weblate/templates/translate.html:309 weblate/templates/translate.html:390 @@ -11667,6 +11814,8 @@ msgid "" "Please fix this by removing duplicated strings with same identifier from the " "translation files." msgstr "" +"Молимо поправите ово уклањањем дуплираних низова са истим идентификатором из " +"преводних фајлова." #: weblate/templates/trans/alert/inexistantfiles.html:4 msgid "Following files are no longer present in the repository:" @@ -11686,6 +11835,8 @@ msgid "" "Any publicly available project should have defined license to indicate what " "terms apply to contributions." msgstr "" +"Сваки јавно доступан пројекат треба да има дефинисану лиценцу да би се " +"назначили услови који важе за доприносе." #: weblate/templates/trans/alert/monolingualglossary.html:4 msgid "" @@ -11703,10 +11854,13 @@ msgstr "" msgid "" "The translation component looks monolingual, but it is not set up to be." msgstr "" +"Компонента превода изгледа монолингвално, али није подешена да буде таква." #: weblate/templates/trans/alert/monolingualtranslation.html:3 msgid "Please add a monolingual base language file in the component settings." msgstr "" +"Молимо вас да додате монолингвалну базну језичку датотеку у подешавањима " +"компоненте." #: weblate/templates/trans/alert/nolibreconditions.html:3 msgid "This project does no longer fit into Libre hosting conditions." @@ -11745,6 +11899,7 @@ msgid "" "Weblate could not parse the translation files while updating the " "translations." msgstr "" +"Веблејт није могао парсирати преводне фајлове приликом ажурирања превода." #: weblate/templates/trans/alert/pushfailure.html:4 msgid "Weblate could not push changes to the upstream repository." @@ -11761,12 +11916,16 @@ msgid "" "The VCS repository has many changes compared to the upstream. Please merge " "the changes manually or set up push to automate this." msgstr "" +"VCS складиште има много промена у поређењу са узводним. Молимо спојите " +"промене ручно или подесите push да аутоматизујете ово." #: weblate/templates/trans/alert/repositoryoutdated.html:4 msgid "" "The VCS repository is not up to date with the upstream one. Please merge " "changes manually or set up hooks to automate this." msgstr "" +"VCS складиште није ажурирано са узводним. Молимо спојите промене ручно или " +"подесите hook-ове да аутоматизујете ово." #: weblate/templates/trans/alert/unsupportedconfiguration.html:9 #: weblate/trans/models/component.py:365 @@ -11778,6 +11937,8 @@ msgid "" "This can happen after turning off support for these features, or when " "dependencies are missing." msgstr "" +"Ово се може десити након искључивања подршке за ове функције, или када " +"недостају зависности." #: weblate/templates/trans/alert/unusedcomponent.html:3 #, python-format @@ -11825,6 +11986,7 @@ msgstr "Управљајте снимцима" #: weblate/templates/trans/alert/updatefailure.html:4 msgid "Weblate could not fetch upstream changes when updating the repository." msgstr "" +"Веблејт није могао да преузме узводне промене приликом ажурирања складишта." #: weblate/templates/trans/alert/updatefailure.html:13 #, fuzzy @@ -11906,13 +12068,14 @@ msgstr "Преведи документ" #: weblate/templates/trans/component_create.html:40 msgid "Start from scratch" -msgstr "" +msgstr "Почните од почетка" #: weblate/templates/trans/component_create.html:51 msgid "" "Create a new translation component from remote version control system " "repository." msgstr "" +"Креирајте нову компоненту превода из складишта система за контролу верзија." #: weblate/templates/trans/component_create.html:53 #: weblate/templates/trans/component_create.html:66 @@ -11930,24 +12093,32 @@ msgid "" "Create a new translation component from a repository already cloned in " "Weblate." msgstr "" +"Креирајте нову компоненту превода из складишта које је већ клонирано у " +"Веблејту." #: weblate/templates/trans/component_create.html:77 msgid "" "Create a new translation component for an additional branch of the existing " "translation component." msgstr "" +"Креирајте нову компоненту превода за додатну грану постојеће компоненте " +"превода." #: weblate/templates/trans/component_create.html:79 msgid "" "The new component will be created for the selected branch with the exact " "same configuration as the existing one." msgstr "" +"Нова компонента ће бити креирана за изабрану грану са тачно истом " +"конфигурацијом као постојећа." #: weblate/templates/trans/component_create.html:91 msgid "" "Create a new translation component from an uploaded ZIP file containing " "strings for translation." msgstr "" +"Креирајте нову компоненту превода из отпремљеног ZIP фајла који садржи " +"низове за превод." #: weblate/templates/trans/component_create.html:92 msgid "The filenames within the archive need to have locale codes in them." @@ -11968,12 +12139,15 @@ msgid "" "Create a ZIP file containing any existing translation files you have, and " "%(link_start)supload it%(link_end)s." msgstr "" +"Креирајте ZIP фајл који садржи било које постојеће фајлове превода које " +"имате, и %(link_start)sотпремите га%(link_end)s." #: weblate/templates/trans/component_form.html:33 msgid "" "You will be able to edit more options in the component settings after " "creating it." msgstr "" +"Моћи ћете да уредите више опција у подешавањима компоненте након креирања." #: weblate/templates/trans/delete-category-language.html:4 #: weblate/templates/trans/delete-project-language.html:4 @@ -11999,6 +12173,8 @@ msgid "" "This action cannot be undone. This will permanently delete the %(object)s " "project and all related content." msgstr "" +"Ова акција се не може поништити. Ово ће трајно избрисати %(object)s пројекат " +"и сав повезани садржај." #: weblate/templates/trans/delete-category.html:6 #: weblate/templates/trans/delete-project.html:6 @@ -12006,6 +12182,8 @@ msgid "" "This includes, but is not limited to, translation memory, glossaries, or " "user permissions." msgstr "" +"Ово укључује, али није ограничено на, меморију превода, глосаре или " +"корисничка овлашћења." #: weblate/templates/trans/delete-category.html:11 #: weblate/templates/trans/delete-component.html:8 @@ -12031,6 +12209,8 @@ msgid "" "This action cannot be undone. This will permanently delete the %(object)s " "component and all related content." msgstr "" +"Ова акција се не може поништити. Ово ће трајно избрисати %(object)s " +"компоненту и сав повезани садржај." #: weblate/templates/trans/delete-component.html:20 msgid "Component to remove" @@ -12038,7 +12218,7 @@ msgstr "Компоненте за уклањање" #: weblate/templates/trans/delete-form.html:11 msgid "I understand the consequences, delete this translation" -msgstr "" +msgstr "Разумем последице, обриши овај превод" #: weblate/templates/trans/delete-project.html:5 #, python-format @@ -12073,6 +12253,8 @@ msgid "" "This action cannot be undone. This will permanently delete the %(object)s " "translation and all related content." msgstr "" +"Ова акција се не може поништити. Ово ће трајно избрисати %(object)s превод и " +"сав повезани садржај." #: weblate/templates/trans/delete-translation.html:20 msgid "Translation to remove" @@ -12080,7 +12262,7 @@ msgstr "Превод за уклањање" #: weblate/templates/trans/discover-choice.html:3 msgid "The detected file format is not supported." -msgstr "" +msgstr "Откривени формат фајла није подржан." #: weblate/templates/trans/guide/addon.html:3 msgid "Enable add-on:" @@ -12088,7 +12270,7 @@ msgstr "" #: weblate/templates/trans/merge_instructions.html:6 msgid "Typical workflow for fixing merge conflicts" -msgstr "" +msgstr "Типичан ток рада за решавање конфликата спајања" #: weblate/templates/trans/merge_instructions.html:10 msgid "" @@ -12099,14 +12281,15 @@ msgstr "" msgid "" "Commit all pending changes in Weblate and lock the translation component." msgstr "" +"Потврдите све чекајуће промене у Веблејту и закључајте компоненту превода." #: weblate/templates/trans/merge_instructions.html:17 msgid "Add Weblate exported repository as a remote." -msgstr "" +msgstr "Додајте Веблејт извозно складиште као даљинско." #: weblate/templates/trans/merge_instructions.html:21 msgid "Merge Weblate changes and resolve any conflicts." -msgstr "" +msgstr "Спојите Веблејт промене и решите све конфликте." #: weblate/templates/trans/merge_instructions.html:26 #, fuzzy @@ -12126,11 +12309,11 @@ msgstr "" #: weblate/templates/trans/merge_instructions.html:42 msgid "Check the FAQ for info on how to resolve this." -msgstr "" +msgstr "Проверите FAQ за информације о томе како да решите ово." #: weblate/templates/trans/messages_help.html:4 msgid "You can use Django templates markup in the commit messages." -msgstr "" +msgstr "Можете користити Django шаблоне у порукама комита." #: weblate/templates/trans/other-row.html:12 msgid "This translation" @@ -12161,6 +12344,7 @@ msgid "" "Once all its permissions are removed, the user will be removed from the " "project." msgstr "" +"Када се уклоне све његове дозволе, корисник ће бити уклоњен из пројекта." #: weblate/templates/trans/project-access.html:153 #, fuzzy @@ -12174,7 +12358,7 @@ msgstr "Позови новог корисника" #: weblate/templates/trans/project-access.html:171 msgid "Invite" -msgstr "" +msgstr "Позови" #: weblate/templates/trans/project-access.html:235 #, python-format @@ -12271,6 +12455,10 @@ msgid "" "a different location. If the repository content is different, it might be " "better to create new translation component." msgstr "" +"При промени URL-а складишта, постојећи садржај ће бити спојен са новим " +"складиштем. Ово ће радити без проблема ако је складиште премештено на другу " +"локацију. Ако је садржај складишта другачији, можда је боље креирати нову " +"компоненту превода." #: weblate/templates/translate.html:51 #, fuzzy @@ -12293,7 +12481,7 @@ msgstr "" #: weblate/templates/translate.html:71 weblate/templates/zen-units.html:19 msgid "Copy permalink" -msgstr "" +msgstr "Копирај сталну везу" #: weblate/templates/translate.html:75 msgid "Forbidden glossary term" @@ -12457,11 +12645,11 @@ msgstr "Предложена измена" #: weblate/templates/translate.html:391 msgid "Origin" -msgstr "" +msgstr "Порекло" #: weblate/templates/translate.html:392 msgid "Similarity" -msgstr "" +msgstr "Сличност" #: weblate/templates/translate.html:436 weblate/trans/forms.py:1102 msgid "New comment" @@ -12470,6 +12658,7 @@ msgstr "Нови коментар" #: weblate/templates/translate.html:440 msgid "Comment on this string for fellow translators and developers to read." msgstr "" +"Коментаришите ову ниску да би је прочитали остали преводиоци и програмери." #: weblate/templates/translate.html:462 msgid "Things to check" @@ -12573,7 +12762,7 @@ msgstr "Старост изворне ниске" #: weblate/templates/translate.html:712 #, python-format msgid "%(filename)s, string %(position)s" -msgstr "" +msgstr "%(filename)s, ниска %(position)s" #: weblate/templates/translate.html:714 #, python-format @@ -12619,7 +12808,7 @@ msgstr "Преузми превод као CSV" #: weblate/templates/translation.html:55 weblate/templates/translation.html:402 msgid "Customize download" -msgstr "" +msgstr "Прилагоди преузимање" #: weblate/templates/translation.html:57 msgid "Upload translation" @@ -12681,6 +12870,8 @@ msgid "" "translation engines to get the best possible translations and applies them " "in this project." msgstr "" +"Аутоматски превод путем машинског превода користи активне моторе за машински " +"превод да би добио најбоље могуће преводе и примењује их у овом пројекту." #: weblate/templates/translation.html:305 #: weblate/templates/translation.html:331 @@ -12688,6 +12879,8 @@ msgid "" "You can add a new translation string here, it will automatically appear in " "all translations." msgstr "" +"Можете додати нови стринг превода овде, а он ће се аутоматски појавити у " +"свим преводима." #: weblate/templates/translation.html:357 #, fuzzy @@ -12727,7 +12920,7 @@ msgstr "" msgid "" "The message is shown on the translation page, until its given expiry, or " "deletion." -msgstr "" +msgstr "Порука се приказује на страници за превођење, до истека или брисања." #: weblate/templates/weblate_auth/invitation_detail.html:9 #, fuzzy @@ -12780,7 +12973,7 @@ msgstr "Промоција пројеката превода" #: weblate/templates/widgets.html:14 #, python-format msgid "You can point newcomers to the introduction page at %(engage_link)s." -msgstr "" +msgstr "Можете упутити новајлије на уводну страницу на %(engage_link)s." #: weblate/templates/widgets.html:17 msgid "Promoting specific translations" @@ -12791,6 +12984,8 @@ msgid "" "Besides promoting the whole translation project, you can also choose a " "specific language or component to promote:" msgstr "" +"Поред промоције целог преводилачког пројекта, можете одабрати и одређени " +"језик или компоненту за промоцију:" #: weblate/templates/widgets.html:24 msgid "Image widgets" @@ -12802,6 +12997,9 @@ msgid "" "They can increase the visibility of your translation projects and bring in " "new contributors." msgstr "" +"Можете користити следеће виџете за промоцију превода вашег пројекта. Они " +"могу повећати видљивост ваших преводилачких пројеката и довести нове " +"сараднике." #: weblate/templates/widgets.html:47 msgid "Color variants:" @@ -12887,7 +13085,7 @@ msgstr "" #: weblate/trans/autofixes/custom.py:31 msgid "Apostrophes in Java MessageFormat" -msgstr "" +msgstr "Апострофи у Java MessageFormat" #: weblate/trans/autofixes/whitespace.py:21 msgid "Trailing and leading whitespace" @@ -13021,7 +13219,7 @@ msgstr "" #: weblate/trans/forms.py:188 msgid "Invalid checksum specified!" -msgstr "" +msgstr "Невалидан контролни збир наведен!" #: weblate/trans/forms.py:245 weblate/trans/forms.py:253 msgid "Toggle text direction" @@ -13039,11 +13237,11 @@ msgstr "Филтер претраге" #: weblate/trans/forms.py:433 msgid "Please choose a valid filter type." -msgstr "" +msgstr "Молимо изаберите важећи тип филтера." #: weblate/trans/forms.py:464 msgid "The string you wanted to translate is no longer available." -msgstr "" +msgstr "Ниска коју сте желели да преведете више није доступна." #: weblate/trans/forms.py:479 msgid "Needs editing" @@ -13054,6 +13252,8 @@ msgid "" "Strings are usually marked as \"Needs editing\" after the source string is " "updated, or when marked as such manually." msgstr "" +"Ниске су обично означене као \"Потребно уређивање\" након што се ажурира " +"изворна ниска, или када се ручно означе као такве." #: weblate/trans/forms.py:496 msgid "Review state" @@ -13061,7 +13261,7 @@ msgstr "Стање прегледа" #: weblate/trans/forms.py:509 weblate/trans/models/unit.py:400 msgid "Additional explanation to clarify meaning or usage of the string." -msgstr "" +msgstr "Додатно објашњење за разјашњење значења или употребе низа." #: weblate/trans/forms.py:560 #, fuzzy @@ -13145,7 +13345,7 @@ msgstr "Увези као преведено" #: weblate/trans/forms.py:702 msgid "Conflict handling" -msgstr "" +msgstr "Руковање конфликтима" #: weblate/trans/forms.py:704 msgid "" @@ -13187,7 +13387,7 @@ msgstr "Неуспело ажурирање складишта." #: weblate/trans/forms.py:885 msgid "Could not find merged string." -msgstr "" +msgstr "Ниска није пронађена." #: weblate/trans/forms.py:904 msgid "Could not find the reverted change." @@ -13222,6 +13422,8 @@ msgid "" "Turn on contribution to shared translation memory for the project to get " "access to additional components." msgstr "" +"Укључите допринос заједничкој меморији превода за пројекат да бисте добили " +"приступ додатним компонентама." #: weblate/trans/forms.py:942 msgid "Machine translation engines" @@ -13229,7 +13431,7 @@ msgstr "Уређаји машинског превођења" #: weblate/trans/forms.py:945 msgid "Score threshold" -msgstr "" +msgstr "Праг резултата" #: weblate/trans/forms.py:982 msgid "" @@ -13263,7 +13465,7 @@ msgstr "" #: weblate/trans/forms.py:1091 msgid "Source string comment, suggestions for changes to this string" -msgstr "" +msgstr "Коментар на изворну ниску, предлози за промене ове ниске" #: weblate/trans/forms.py:1096 msgid "Translation comment, discussions with other translators" @@ -13272,7 +13474,7 @@ msgstr "Коментар превода, дискусије са другим п #: weblate/trans/forms.py:1103 weblate/trans/models/announcement.py:72 #: weblate/trans/models/project.py:158 msgid "You can use Markdown and mention users by @username." -msgstr "" +msgstr "Можете користити Markdown и поменути кориснике са @корисничко_име." #: weblate/trans/forms.py:1135 msgid "All components" @@ -13342,7 +13544,7 @@ msgstr "Лиценца" #: weblate/trans/forms.py:1531 msgid "Upstream links" -msgstr "" +msgstr "Везе узводно" #: weblate/trans/forms.py:1533 msgid "Listing and access" @@ -13366,7 +13568,7 @@ msgstr "Поставке контроле верзија" #: weblate/trans/forms.py:1585 msgid "Commit messages" -msgstr "" +msgstr "Поруке комита" #: weblate/trans/forms.py:1601 msgid "Translation files" @@ -13400,14 +13602,14 @@ msgstr "Изаберите фајлове превода за увоз" #: weblate/trans/forms.py:1912 msgid "Specify configuration manually" -msgstr "" +msgstr "Наведите конфигурацију ручно" #: weblate/trans/forms.py:2095 #, python-format msgid "" "You must specify a license for these components to make them publicly " "accessible: %s" -msgstr "" +msgstr "Морате навести лиценцу за ове компоненте да би биле јавно доступне: %s" #: weblate/trans/forms.py:2144 msgid "Access" @@ -13416,7 +13618,7 @@ msgstr "Приступ" #: weblate/trans/forms.py:2191 msgid "" "Uses and contributes to the pool of shared translations between projects." -msgstr "" +msgstr "Користи и доприноси базену заједничких превода између пројеката." #: weblate/trans/forms.py:2259 #, fuzzy @@ -13444,7 +13646,7 @@ msgstr "" #: weblate/trans/forms.py:2303 msgid "Replacement string" -msgstr "" +msgstr "Ниска за замену" #: weblate/trans/forms.py:2400 msgid "Translation key" @@ -13464,6 +13666,7 @@ msgstr "Текст изворног језика" msgid "" "You can edit this later, as with any other string in the source language." msgstr "" +"Можете ово уредити касније, као и било коју другу ниску на изворном језику." #. Translators: Translation context for Gettext #: weblate/trans/forms.py:2425 weblate/trans/models/component.py:3669 @@ -13508,7 +13711,7 @@ msgstr "Тренутно немате дозволе да правите про #: weblate/trans/forms.py:2572 msgid "State to set" -msgstr "" +msgstr "Стање за подешавање" #: weblate/trans/forms.py:2573 msgid "Do not change" @@ -13524,7 +13727,7 @@ msgstr "Заставице за уклањање" #: weblate/trans/forms.py:2583 msgid "Labels to add" -msgstr "" +msgstr "Ознаке за додавање" #: weblate/trans/forms.py:2589 msgid "Labels to remove" @@ -13536,7 +13739,7 @@ msgstr "Прихватам споразум о дориносиоцима" #: weblate/trans/forms.py:2663 msgid "The slug does not match the one marked for deletion!" -msgstr "" +msgstr "Слаг не одговара оном означеном за брисање!" #: weblate/trans/forms.py:2669 weblate/trans/forms.py:2688 #: weblate/trans/forms.py:2699 weblate/trans/forms.py:2715 @@ -13628,26 +13831,27 @@ msgstr "Интеграција у контролу верзија" #: weblate/trans/guide.py:81 msgid "Configure repository hooks for automated flow of updates to Weblate." msgstr "" +"Конфигуришите куке складишта за аутоматизовани ток ажурирања на Веблејт." #: weblate/trans/guide.py:102 msgid "Configure push URL for automated flow of translations from Weblate." -msgstr "" +msgstr "Конфигуришите push URL за аутоматизовани ток превода са Веблејта." #: weblate/trans/guide.py:116 msgid "Building community" -msgstr "" +msgstr "Изградња заједнице" #: weblate/trans/guide.py:125 msgid "Define translation instructions to give translators a guideline." -msgstr "" +msgstr "Дефинишите упутства за превод да бисте дали смернице преводиоцима." #: weblate/trans/guide.py:143 msgid "Make your translations available under a libre license." -msgstr "" +msgstr "Учини своје преводе доступним под либре лиценцом." #: weblate/trans/guide.py:160 weblate/trans/templatetags/translations.py:1080 msgid "Fix this component to clear its alerts." -msgstr "" +msgstr "Поправите ову компоненту да бисте очистили њена упозорења." #: weblate/trans/guide.py:173 msgid "Provide context to the translators" @@ -13655,7 +13859,7 @@ msgstr "Омогућава контекст за преводиоце" #: weblate/trans/guide.py:181 msgid "Add screenshots to show where strings are being used." -msgstr "" +msgstr "Додајте снимке екрана да покажете где се користе низови." #: weblate/trans/guide.py:196 msgid "Use flags to indicate special strings in your translation." @@ -13669,7 +13873,7 @@ msgstr "" #: weblate/trans/guide.py:247 msgid "Workflow customization" -msgstr "" +msgstr "Прилагођавање тока рада" #: weblate/trans/mixins.py:144 #, fuzzy @@ -13709,7 +13913,7 @@ msgstr "Неуспело стапање складишта." #. Translators: Name of an alert #: weblate/trans/models/alert.py:242 msgid "Could not push the repository." -msgstr "" +msgstr "Није могуће послати складиште." #. Translators: Name of an alert #: weblate/trans/models/alert.py:326 @@ -13724,7 +13928,7 @@ msgstr "Неуспело рашчлчањивање фајлова превод #. Translators: Name of an alert #: weblate/trans/models/alert.py:342 msgid "Your billing plan has exceeded its limits." -msgstr "" +msgstr "Ваш план наплате је премашио своје лимите." #. Translators: Name of an alert #: weblate/trans/models/alert.py:348 @@ -13750,7 +13954,7 @@ msgstr "" #. Translators: Name of an alert #: weblate/trans/models/alert.py:404 msgid "Misconfigured monolingual translation." -msgstr "" +msgstr "Погрешно конфигурисан једнојезични превод." #. Translators: Name of an alert #: weblate/trans/models/alert.py:444 @@ -13841,10 +14045,12 @@ msgid "" "The message will be not shown after this date. Use it to announce string " "freeze and translation deadline for next release." msgstr "" +"Порука неће бити приказана после овог датума. Користите је да објавите " +"замрзавање стрингова и рок за превод за следеће издање." #: weblate/trans/models/announcement.py:128 msgid "Notify users" -msgstr "" +msgstr "Обавести кориснике" #: weblate/trans/models/announcement.py:129 msgid "Send notification to subscribed users." @@ -13873,7 +14079,7 @@ msgstr "URL слаг" #: weblate/trans/models/category.py:55 weblate/trans/models/component.py:348 #: weblate/trans/models/componentlist.py:36 weblate/trans/models/project.py:146 msgid "Name used in URLs and filenames." -msgstr "" +msgstr "Име које се користи у URL-овима и именима фајлова." #: weblate/trans/models/category.py:166 msgid "Too deep nesting of categories!" @@ -14414,11 +14620,11 @@ msgstr "Искључи додавање нових превода" #: weblate/trans/models/component.py:126 msgid "Default based on the file format" -msgstr "" +msgstr "Подразумевано на основу формата датотеке." #: weblate/trans/models/component.py:127 msgid "POSIX style using underscore as a separator" -msgstr "" +msgstr "POSIX стил користи доњу црту као сепаратор" #: weblate/trans/models/component.py:130 msgid "POSIX style using underscore as a separator, lower cased" @@ -14426,11 +14632,11 @@ msgstr "" #: weblate/trans/models/component.py:132 msgid "BCP style using hyphen as a separator" -msgstr "" +msgstr "BCP стил користи цртицу као сепаратор" #: weblate/trans/models/component.py:136 msgid "POSIX style using underscore as a separator, including country code" -msgstr "" +msgstr "POSIX стил користећи доњу црту као сепаратор, укључујући код земље" #: weblate/trans/models/component.py:142 msgid "" @@ -14440,7 +14646,7 @@ msgstr "" #: weblate/trans/models/component.py:147 msgid "BCP style using hyphen as a separator, including country code" -msgstr "" +msgstr "BCP стил користећи цртицу као сепаратор, укључујући код земље" #: weblate/trans/models/component.py:151 msgid "BCP style using hyphen as a separator, legacy language codes" @@ -14452,7 +14658,7 @@ msgstr "" #: weblate/trans/models/component.py:154 msgid "Android style" -msgstr "" +msgstr "Android стил." #: weblate/trans/models/component.py:155 msgid "Apple App Store metadata style" @@ -14492,12 +14698,17 @@ msgid "" "translations. You can also choose additional integration with third party " "providers to submit merge requests." msgstr "" +"Систем контроле верзија који се користи за приступ вашем складишту које " +"садржи преводе. Такође можете изабрати додатну интеграцију са трећим " +"провајдерима за подношење захтева за спајање." #: weblate/trans/models/component.py:380 msgid "" "URL of a repository, use weblate://project/component to share it with other " "component." msgstr "" +"УРЛ складишта, користите weblate://project/component да бисте га поделили са " +"другом компонентом." #: weblate/trans/models/component.py:392 msgid "Repository push URL" @@ -14505,7 +14716,7 @@ msgstr "URL за push репозиторијума" #: weblate/trans/models/component.py:395 msgid "URL of a push repository, pushing is turned off if empty." -msgstr "" +msgstr "УРЛ репозиторија за пуштање, пуштање је искључено ако је празно." #: weblate/trans/models/component.py:400 msgid "Repository browser" @@ -14532,7 +14743,7 @@ msgstr "Извезени URL репозиторијума" #: weblate/trans/models/component.py:414 msgid "URL of repository where users can fetch changes from Weblate" -msgstr "" +msgstr "УРЛ складишта где корисници могу преузети промене из Веблејта." #: weblate/trans/models/component.py:419 msgid "Source string bug reporting address" @@ -14543,6 +14754,8 @@ msgid "" "E-mail address for reports on errors in source strings. Leave empty for no e-" "mails." msgstr "" +"Адреса е-поште за извештаје о грешкама у изворним низовима. Оставите празно " +"за без е-поште." #: weblate/trans/models/component.py:429 msgid "Repository branch to translate" @@ -14550,11 +14763,11 @@ msgstr "Грана складишта за превођење" #: weblate/trans/models/component.py:434 msgid "Push branch" -msgstr "" +msgstr "Пошаљи грану" #: weblate/trans/models/component.py:437 msgid "Branch for pushing changes, leave empty to use repository branch" -msgstr "" +msgstr "Грана за слање промена, оставите празно да користите грану складишта" #: weblate/trans/models/component.py:447 msgid "" @@ -14636,6 +14849,8 @@ msgid "" "Automatically accept suggestions with this number of votes, use 0 to turn it " "off." msgstr "" +"Аутоматски прихватите предлоге са овим бројем гласова, користите 0 да бисте " +"га искључили." #: weblate/trans/models/component.py:550 weblate/trans/models/unit.py:387 msgid "Translation flags" @@ -14651,13 +14866,15 @@ msgstr "Обавезне провере" #: weblate/trans/models/component.py:560 msgid "List of checks which can not be ignored." -msgstr "" +msgstr "Листа провера које се не могу игнорисати." #: weblate/trans/models/component.py:578 msgid "" "User agreement which needs to be approved before a user can translate this " "component." msgstr "" +"Кориснички уговор који мора бити одобрен пре него што корисник може превести " +"ову компоненту." #: weblate/trans/models/component.py:585 msgid "Adding new translation" @@ -14676,6 +14893,8 @@ msgid "" "Customize language code used to generate the filename for translations " "created by Weblate." msgstr "" +"Прилагодите код језика који се користи за генерисање имена фајла за преводе " +"које креира Веблејт." #: weblate/trans/models/component.py:603 msgid "Manage strings" @@ -14697,6 +14916,8 @@ msgid "" "Define whether Weblate should merge the upstream repository or rebase " "changes onto it." msgstr "" +"Дефинишите да ли Веблејт треба да споји репозиторијум или да рефакторише " +"промене на њега." #: weblate/trans/models/component.py:624 msgid "Commit message when translating" @@ -14709,6 +14930,8 @@ msgid "" "You can use template language for various info, please consult the " "documentation for more details." msgstr "" +"Можете користити језик шаблона за различите информације, молимо вас да " +"консултујете документацију за више детаља." #: weblate/trans/models/component.py:633 msgid "Commit message when adding translation" @@ -14716,11 +14939,11 @@ msgstr "Порука о потврди приликом додавања пре #: weblate/trans/models/component.py:642 msgid "Commit message when removing translation" -msgstr "" +msgstr "Порука комита приликом уклањања превода" #: weblate/trans/models/component.py:651 msgid "Commit message when merging translation" -msgstr "" +msgstr "Порука комита при спајању превода" #: weblate/trans/models/component.py:660 msgid "Commit message when add-on makes a change" @@ -14740,16 +14963,17 @@ msgstr "Да ли ће се складиште одаслати на горе п #: weblate/trans/models/component.py:685 msgid "Age of changes to commit" -msgstr "" +msgstr "Старост промена за комитовање" #: weblate/trans/models/component.py:689 msgid "" "Time in hours after which any pending changes will be committed to the VCS." msgstr "" +"Време у сатима након којег ће све нерешене промене бити комитоване у VCS." #: weblate/trans/models/component.py:694 msgid "Lock on error" -msgstr "" +msgstr "Закључај на грешку" #: weblate/trans/models/component.py:697 msgid "Whether the component should be locked on repository errors." @@ -14779,11 +15003,11 @@ msgstr "Варијанте регуларног израза" #: weblate/trans/models/component.py:724 msgid "Regular expression used to determine variants of a string." -msgstr "" +msgstr "Регуларни израз који се користи за одређивање варијанти низа." #: weblate/trans/models/component.py:733 msgid "Components with higher priority are offered first to translators." -msgstr "" +msgstr "Компоненте са вишим приоритетом се прво нуде преводиоцима." #: weblate/trans/models/component.py:737 msgid "Restricted component" @@ -14793,6 +15017,7 @@ msgstr "Ограничена компонента" msgid "" "Restrict access to the component to only those explicitly given permission." msgstr "" +"Ограничите приступ компоненти само на оне којима је експлицитно дата дозвола." #: weblate/trans/models/component.py:748 msgid "Share in projects" @@ -14815,12 +15040,16 @@ msgid "" "Could not verify SSH host key, please add them in SSH page in the admin " "interface." msgstr "" +"Нисам могао да верификујем SSH кључ хоста, молимо додајте их на SSH страници " +"у администраторском интерфејсу." #: weblate/trans/models/component.py:1522 msgid "" "The repository requires authentication, please specify credentials in the " "URL or use SSH access instead." msgstr "" +"Складиште захтева аутентификацију, молимо наведите акредитиве у URL-у или " +"користите SSH приступ уместо тога." #: weblate/trans/models/component.py:1528 #, python-format @@ -14841,12 +15070,12 @@ msgstr "" #: weblate/trans/models/component.py:1810 #, python-format msgid "Push is turned off for %s." -msgstr "" +msgstr "Push је искључен за %s." #: weblate/trans/models/component.py:1872 #, python-format msgid "Could not reset to remote branch on %s." -msgstr "" +msgstr "Нисам могао да ресетујем на удаљену грану на %s." #: weblate/trans/models/component.py:1912 #, python-format @@ -14856,7 +15085,7 @@ msgstr "Нисам успео да очистим складиште: %s." #: weblate/trans/models/component.py:2156 #, python-format msgid "Could not rebase local branch onto remote branch %s." -msgstr "" +msgstr "Нисам могао да ребејзујем локалну грану на удаљену грану %s." #: weblate/trans/models/component.py:2162 #, python-format @@ -14883,10 +15112,11 @@ msgstr "" #: weblate/trans/models/component.py:2759 msgid "Invalid link to a Weblate project, cannot link to linked repository!" msgstr "" +"Неважећа веза до Веблејт пројекта, не може се повезати на повезано складиште!" #: weblate/trans/models/component.py:2768 msgid "Invalid link to a Weblate project, cannot link it to itself!" -msgstr "" +msgstr "Неважећа веза до Веблејт пројекта, не може се повезати сама на себе!" #: weblate/trans/models/component.py:2779 msgid "" @@ -14923,6 +15153,8 @@ msgid "" "You have set up Weblate to add new translation files, but did not provide a " "base file to do that." msgstr "" +"Подесили сте Веблејт да дода нове датотеке превода, али нисте пружили базну " +"датотеку за то." #: weblate/trans/models/component.py:2878 #, fuzzy, python-format @@ -14942,7 +15174,7 @@ msgstr "" #: weblate/trans/models/component.py:2919 msgid "" "An intermediate language file can not be used without an editing template." -msgstr "" +msgstr "Посредни језички фајл не може се користити без шаблона за уређивање." #: weblate/trans/models/component.py:2911 msgid "" @@ -14952,12 +15184,12 @@ msgstr "" #: weblate/trans/models/component.py:2926 msgid "Using a .pot file as base file is unsupported." -msgstr "" +msgstr "Користење .pot датотеке као базне датотеке није подржано." #: weblate/trans/models/component.py:2942 #, python-format msgid "Could not parse translation base file: %s" -msgstr "" +msgstr "Нисмо могли да парсирамо базну датотеку превода: %s" #: weblate/trans/models/component.py:2953 #, python-brace-format @@ -14992,6 +15224,8 @@ msgid "" "Please either fill in an instruction URL or use a different option for " "adding a new language." msgstr "" +"Молимо или попуните URL инструкције или користите другу опцију за додавање " +"новог језика." #: weblate/trans/models/component.py:3039 msgid "" @@ -15006,6 +15240,7 @@ msgstr "" #: weblate/trans/models/component.py:3082 weblate/trans/models/workflow.py:58 msgid "Accepting suggestions automatically only works with voting turned on." msgstr "" +"Аутоматско прихватање предлога функционише само ако је гласање укључено." #: weblate/trans/models/component.py:3466 msgid "Could not add new translation file." @@ -15044,7 +15279,7 @@ msgstr "Унос са истим називом већ постоји." #: weblate/trans/models/component.py:3553 msgid "The given language is filtered by the language filter." -msgstr "" +msgstr "Дати језик је филтриран филтером језика." #: weblate/trans/models/component.py:3579 msgid "Translation file already exists!" @@ -15058,7 +15293,7 @@ msgstr "Превод ће бити освежен у позадини." #: weblate/trans/models/component.py:3671 weblate/utils/views.py:187 msgctxt "Translation key" msgid "Key" -msgstr "" +msgstr "Кључ" #: weblate/trans/models/component.py:3755 msgid "Add new translation string" @@ -15076,6 +15311,8 @@ msgstr "Прикажи на контролној табли" msgid "" "When enabled this component list will be shown as a tab on the dashboard" msgstr "" +"Када је омогућено, ова листа компоненти ће бити приказана као таб на " +"контролној табли" #: weblate/trans/models/componentlist.py:72 msgid "Project regular expression" @@ -15083,7 +15320,7 @@ msgstr "Регуларни израз за пројекат" #: weblate/trans/models/componentlist.py:76 msgid "Regular expression which is used to match project slug." -msgstr "" +msgstr "Регуларни израз који се користи за подударање са слагом пројекта." #: weblate/trans/models/componentlist.py:80 msgid "Component regular expression" @@ -15091,7 +15328,7 @@ msgstr "Регуларни израз за компоненту" #: weblate/trans/models/componentlist.py:84 msgid "Regular expression which is used to match component slug." -msgstr "" +msgstr "Регуларни израз који се користи за подударање са слагом компоненте." #: weblate/trans/models/componentlist.py:89 msgid "Component list to assign" @@ -15109,11 +15346,11 @@ msgstr "Јавни" #: weblate/trans/models/project.py:130 msgid "Protected" -msgstr "" +msgstr "Заштићено" #: weblate/trans/models/project.py:131 msgid "Private" -msgstr "" +msgstr "Приватно" #: weblate/trans/models/project.py:132 msgid "Custom" @@ -15134,6 +15371,7 @@ msgstr "Постави заглавље „Language-Team“" #: weblate/trans/models/project.py:165 msgid "Lets Weblate update the \"Language-Team\" file header of your project." msgstr "" +"Омогућава Веблејту да ажурира заглавље \"Language-Team\" вашег пројекта." #: weblate/trans/models/project.py:169 msgid "Use shared translation memory" @@ -15149,12 +15387,13 @@ msgstr "Допуни дељену преводилачку меморију" #: weblate/trans/models/project.py:179 msgid "Contributes to the pool of shared translations between projects." -msgstr "" +msgstr "Доприноси базену заједничких превода између пројеката." #: weblate/trans/models/project.py:188 msgid "" "How to restrict access to this project is detailed in the documentation." msgstr "" +"Како ограничити приступ овом пројекту је детаљно описано у документацији." #: weblate/trans/models/project.py:196 msgid "" @@ -15168,7 +15407,7 @@ msgstr "Укључи оверавање" #: weblate/trans/models/project.py:203 weblate/trans/models/workflow.py:25 msgid "Requires dedicated reviewers to approve translations." -msgstr "" +msgstr "Захтева посебне рецензенте за одобравање превода." #: weblate/trans/models/project.py:206 msgid "Enable source reviews" @@ -15176,7 +15415,7 @@ msgstr "Укључи оверавање извора" #: weblate/trans/models/project.py:209 msgid "Requires dedicated reviewers to approve source strings." -msgstr "" +msgstr "Захтева посебне рецензенте да одобре изворне ниске." #: weblate/trans/models/project.py:213 msgid "Enable hooks" @@ -15357,7 +15596,7 @@ msgstr "Дуга црта" #: weblate/trans/specialchars.py:288 msgid "User configured character: {}" -msgstr "" +msgstr "Кориснички конфигурисан карактер: {}" #: weblate/trans/tasks.py:488 #, fuzzy, python-format @@ -15482,9 +15721,9 @@ msgstr "пре секунд" #, python-format msgid "%(count)s second ago" msgid_plural "%(count)s seconds ago" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "%(count)s секунда раније" +msgstr[1] "%(count)s секундe раније" +msgstr[2] "%(count)s секунди раније" #: weblate/trans/templatetags/translations.py:677 msgid "a minute ago" @@ -15494,9 +15733,9 @@ msgstr "пре минут" #, python-format msgid "%(count)s minute ago" msgid_plural "%(count)s minutes ago" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "%(count)s минут раније" +msgstr[1] "%(count)s минуте раније" +msgstr[2] "%(count)s минута раније" #: weblate/trans/templatetags/translations.py:683 msgid "an hour ago" @@ -15506,9 +15745,9 @@ msgstr "пре сат времена" #, python-format msgid "%(count)s hour ago" msgid_plural "%(count)s hours ago" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "%(count)s сат раније" +msgstr[1] "%(count)s сата раније" +msgstr[2] "%(count)s сати раније" #: weblate/trans/templatetags/translations.py:696 msgid "a year from now" @@ -15518,9 +15757,9 @@ msgstr "за годину" #, python-format msgid "%(count)s year from now" msgid_plural "%(count)s years from now" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "%(count)s годину од сада" +msgstr[1] "%(count)s године од сада" +msgstr[2] "%(count)s година од сада" #: weblate/trans/templatetags/translations.py:703 msgid "a month from now" @@ -15530,17 +15769,17 @@ msgstr "месец од сада" #, python-format msgid "%(count)s month from now" msgid_plural "%(count)s months from now" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "%(count)s месец од сада" +msgstr[1] "%(count)s месеца од сада" +msgstr[2] "%(count)s месеци од сада" #: weblate/trans/templatetags/translations.py:710 #, python-format msgid "%(count)s week from now" msgid_plural "%(count)s weeks from now" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "%(count)s недељу од сада" +msgstr[1] "%(count)s недеље од сада" +msgstr[2] "%(count)s недеља од сада" #: weblate/trans/templatetags/translations.py:714 msgid "tomorrow" @@ -15548,15 +15787,15 @@ msgstr "сутра" #: weblate/trans/templatetags/translations.py:716 msgid "a week from now" -msgstr "" +msgstr "недељу дана од сада" #: weblate/trans/templatetags/translations.py:718 #, python-format msgid "%(count)s day from now" msgid_plural "%(count)s days from now" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "%(count)s дан од сада" +msgstr[1] "%(count)s дана од сада" +msgstr[2] "%(count)s дана од сада" #: weblate/trans/templatetags/translations.py:724 msgid "a second from now" @@ -15566,9 +15805,9 @@ msgstr "секунда од сада" #, python-format msgid "%(count)s second from now" msgid_plural "%(count)s seconds from now" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "%(count)s секунда од сада" +msgstr[1] "%(count)s секунде од сада" +msgstr[2] "%(count)s секунди од сада" #: weblate/trans/templatetags/translations.py:731 msgid "a minute from now" @@ -15578,9 +15817,9 @@ msgstr "минут од сада" #, python-format msgid "%(count)s minute from now" msgid_plural "%(count)s minutes from now" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "%(count)s минут од сада" +msgstr[1] "%(count)s минуте од сада" +msgstr[2] "%(count)s минута од сада" #: weblate/trans/templatetags/translations.py:737 msgid "an hour from now" @@ -15590,9 +15829,9 @@ msgstr "сат од сада" #, python-format msgid "%(count)s hour from now" msgid_plural "%(count)s hours from now" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "%(count)s сат од сада" +msgstr[1] "%(count)s сата од сада" +msgstr[2] "%(count)s сати од сада" #: weblate/trans/templatetags/translations.py:869 msgctxt "String state" @@ -15631,7 +15870,7 @@ msgstr "Ажурирам компоненту превода…" #: weblate/trans/templatetags/translations.py:1100 msgid "Some of the components within this project have alerts." -msgstr "" +msgstr "Неке од компоненти унутар овог пројекта имају упозорења." #: weblate/trans/templatetags/translations.py:1144 msgid "You administrate this project." @@ -15656,7 +15895,7 @@ msgstr "Дељено из пројекта %s." #: weblate/trans/templatetags/translations.py:1207 msgid "enabled" -msgstr "" +msgstr "омогућено" #. Translators: Number format, in millions (mega) #: weblate/trans/templatetags/translations.py:1250 @@ -15748,7 +15987,7 @@ msgstr "" #: weblate/trans/views/about.py:22 msgid "Keys" -msgstr "" +msgstr "Кључеви" #: weblate/trans/views/about.py:23 weblate/trans/views/about.py:130 #, fuzzy @@ -15854,6 +16093,8 @@ msgid "" "The project you were looking for has been removed, however you are welcome " "to contribute to other ones." msgstr "" +"Пројекат који сте тражили је уклоњен, али сте добродошли да допринесете " +"другим пројектима." #: weblate/trans/views/dashboard.py:160 msgid "" @@ -15865,7 +16106,7 @@ msgstr "" #: weblate/trans/views/dashboard.py:174 msgid "Please set your full name and e-mail in your profile." -msgstr "" +msgstr "Молимо поставите своје пуно име и е-пошту у свом профилу." #: weblate/trans/views/edit.py:79 #, python-format @@ -15904,7 +16145,7 @@ msgstr "" msgid "" "The translation has been saved, however there are some newly failing checks: " "{0}" -msgstr "" +msgstr "Превод је сачуван, међутим постоје неки нови неуспели прегледи: {0}" #: weblate/trans/views/edit.py:446 msgid "Invalid merge request!" @@ -15912,7 +16153,7 @@ msgstr "Неисправан захтев за стапање!" #: weblate/trans/views/edit.py:466 msgid "Invalid revert request!" -msgstr "" +msgstr "Невалидан захтев за враћање!" #: weblate/trans/views/edit.py:478 msgid "Can not revert to empty translation!" @@ -15987,7 +16228,7 @@ msgstr "Не могу да рашчланим ниску упита: {}" #: weblate/trans/views/error.py:28 msgid "Bad Request" -msgstr "" +msgstr "Лош захтев" #: weblate/trans/views/files.py:197 msgid "Access denied." @@ -16360,6 +16601,8 @@ msgid "" "The format strings are no longer supported, please use the template language " "instead." msgstr "" +"Формат стрингова више није подржан, молимо вас да користите језик шаблона " +"уместо тога." #: weblate/utils/search.py:162 msgid "Unsupported state: {}" @@ -16367,7 +16610,7 @@ msgstr "Неподржано стање: {}" #: weblate/utils/search.py:259 msgid "Invalid timestamp: {}" -msgstr "" +msgstr "Неважећи временски жиг: {}" #: weblate/utils/search.py:336 msgid "Invalid regular expression: {}" @@ -16376,7 +16619,7 @@ msgstr "Неисправан регуларни израз: {}" #: weblate/utils/state.py:17 msgctxt "String state" msgid "Empty" -msgstr "" +msgstr "Празно" #: weblate/utils/state.py:18 msgctxt "String state" @@ -16418,6 +16661,8 @@ msgid "" "Regular expression is missing named group \"{0}\", the simplest way to " "define it is {1}." msgstr "" +"Регуларни израз недостаје именована група \"{0}\", најједноставнији начин да " +"се дефинише је {1}." #: weblate/utils/validators.py:114 msgid "Invalid image!" @@ -16430,19 +16675,19 @@ msgstr "Неподржан тип слике: %s" #: weblate/utils/validators.py:131 msgid "The image is too big, please crop or scale it down." -msgstr "" +msgstr "Слика је превелика, молимо исеците је или смањите." #: weblate/utils/validators.py:150 msgid "Please avoid using special characters in the full name." -msgstr "" +msgstr "Молимо избегавајте коришћење специјалних знакова у пуном имену." #: weblate/utils/validators.py:154 msgid "Name consists only of disallowed characters." -msgstr "" +msgstr "Име се састоји само од недозвољених карактера." #: weblate/utils/validators.py:169 msgid "The username can not start with a full stop." -msgstr "" +msgstr "Корисничко име не може почети са тачком." #: weblate/utils/validators.py:180 weblate/utils/validators.py:188 msgid "Enter a valid e-mail address." @@ -16464,15 +16709,16 @@ msgstr "Не могу да оценим формулу множине: {}" #: weblate/utils/validators.py:214 msgid "The filename can not contain reference to a parent directory." -msgstr "" +msgstr "Име фајла не може садржати референцу на родитељски директоријум." #: weblate/utils/validators.py:217 msgid "The filename can not be an absolute path." -msgstr "" +msgstr "Име фајла не може бити апсолутна путања." #: weblate/utils/validators.py:223 msgid "The filename should be as simple as possible. Maybe you want to use: {}" msgstr "" +"Име фајла треба да буде што једноставније. Можда желите да користите: {}" #: weblate/utils/validators.py:261 weblate/utils/validators.py:278 msgid "This name is prohibited" @@ -16499,7 +16745,7 @@ msgstr "Позиција" #: weblate/utils/views.py:184 msgid "Number of words" -msgstr "" +msgstr "Број речи" #: weblate/utils/views.py:185 msgid "Number of comments" @@ -16548,7 +16794,7 @@ msgstr "Предаје измене у локално складиште" #: weblate/vcs/git.py:768 msgid "Git with force push" -msgstr "" +msgstr "Git са force push" #: weblate/vcs/git.py:772 #, fuzzy @@ -16676,7 +16922,7 @@ msgstr "Активациони жетон" #: weblate/wladmin/forms.py:19 msgid "" "Please enter the activation token obtained when making the subscription." -msgstr "" +msgstr "Молимо унесите активациони токен добијен приликом претплате." #: weblate/wladmin/forms.py:29 msgid "Port" @@ -16722,7 +16968,7 @@ msgstr "" #: weblate/wladmin/models.py:123 msgid "Community support" -msgstr "" +msgstr "Подршка заједнице" #: weblate/wladmin/models.py:124 msgid "Hosted service" @@ -16752,11 +16998,11 @@ msgstr "" #: weblate/wladmin/models.py:306 msgid "Backup performed" -msgstr "" +msgstr "Резервна копија извршена" #: weblate/wladmin/models.py:307 msgid "Backup failed" -msgstr "" +msgstr "Резервна копија није успела" #: weblate/wladmin/models.py:308 msgid "Deleted the oldest backups" @@ -16776,7 +17022,7 @@ msgstr "Веблејт администрација" #: weblate/wladmin/sites.py:45 msgid "Object listing turned off" -msgstr "" +msgstr "Листа објеката је искључена" #: weblate/wladmin/templates/admin/base.html:6 msgid "Return to Weblate" @@ -16788,7 +17034,7 @@ msgstr "Документација за брзи увоз" #: weblate/wladmin/templates/admin/trans/change_form.html:37 msgid "Required fields are marked in bold." -msgstr "" +msgstr "Обавезна поља су означена подебљано." #: weblate/wladmin/templates/admin/trans/change_form.html:39 msgid "" @@ -16833,6 +17079,8 @@ msgid "" "Could not activate your installation. Please ensure your activation token is " "correct." msgstr "" +"Нисам могао да активирам вашу инсталацију. Молимо проверите да ли је ваш " +"активациони токен исправан." #: weblate/wladmin/views.py:230 #, python-format @@ -16845,7 +17093,7 @@ msgstr "Активације је готова." #: weblate/wladmin/views.py:274 msgid "Backup process triggered" -msgstr "" +msgstr "Процес резервне копије покренут" #: weblate/wladmin/views.py:296 msgid "Could not dismiss the configuration error!" From 293bcf6fb581457756bfa090471d6c2150474db8 Mon Sep 17 00:00:00 2001 From: Christian Wia Date: Sun, 29 Sep 2024 20:28:03 +0000 Subject: [PATCH 03/61] Translated using Weblate (French) Currently translated at 42.0% (4036 of 9608 strings) Translation: Weblate/Documentation Translate-URL: https://hosted.weblate.org/projects/weblate/documentation/fr/ --- docs/locales/fr/LC_MESSAGES/docs.po | 40 +++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/docs/locales/fr/LC_MESSAGES/docs.po b/docs/locales/fr/LC_MESSAGES/docs.po index 4c43be95b5b9..b615c24c0bd7 100644 --- a/docs/locales/fr/LC_MESSAGES/docs.po +++ b/docs/locales/fr/LC_MESSAGES/docs.po @@ -21,7 +21,7 @@ msgstr "" "Project-Id-Version: Weblate 5.8\n" "Report-Msgid-Bugs-To: https://github.com/WeblateOrg/weblate/issues\n" "POT-Creation-Date: 2024-09-20 14:27+0200\n" -"PO-Revision-Date: 2024-09-28 08:33+0000\n" +"PO-Revision-Date: 2024-09-30 13:16+0000\n" "Last-Translator: Christian Wia \n" "Language-Team: French \n" @@ -7641,6 +7641,10 @@ msgid "" "checks can be adjusted using :setting:`CHECK_LIST`, and you can also add " "custom checks." msgstr "" +"Beaucoup de contrôles qualité sont intégrés (voir :ref:`checks`) bien qu'ils " +"ne puissent pas tous couvrir ce que vous souhaiteriez vérifier. La liste des " +"contrôles réalisés peut être ajustée en utilisant :setting:`CHECK_LIST`et " +"vous pouvez aussi ajouter des contrôles personnalisés." #: ../../admin/checks.rst:389 msgid "Subclass the `weblate.checks.Check`" @@ -8062,6 +8066,8 @@ msgid "" "Password changes made prior to Weblate 2.15 will not be accounted for in " "this policy." msgstr "" +"Les modifications du mot de passe faites avant Weblate 2.15 ne sont pas " +"soumises à cette règle." #: ../../admin/config.rst:164 msgid "AUTOFIX_LIST" @@ -8110,6 +8116,8 @@ msgstr "``weblate.trans.autofixes.chars.RemoveZeroSpace``" #: ../../admin/config.rst:180 msgid "Removes zero-width space characters if the source does not contain any." msgstr "" +"Supprime les caractères espaces de largeur nulle si la source n'en contient " +"aucun." #: ../../admin/config.rst:181 msgid "``weblate.trans.autofixes.chars.RemoveControlChars``" @@ -8117,7 +8125,7 @@ msgstr "``weblate.trans.autofixes.chars.RemoveControlChars``" #: ../../admin/config.rst:182 msgid "Removes control characters if the source does not contain any." -msgstr "" +msgstr "Supprime les caractères de contrôle si la source n'en contient aucun." #: ../../admin/config.rst:183 #, fuzzy @@ -8139,10 +8147,12 @@ msgid "" "Removes unsafe HTML markup from strings flagged as ``safe-html`` (see :ref:" "`check-safe-html`)." msgstr "" +"Supprime le balisage HTML non sécurisé à l'intéreur des chaînes marquées " +"``safe-html`` (voir :ref:`check-safe-html`)." #: ../../admin/config.rst:188 msgid "You can select which ones to use:" -msgstr "" +msgstr "Vous pouvez choisir ce qui doit être utilisé :" #: ../../admin/config.rst:199 msgid ":ref:`autofix`, :ref:`custom-autofix`" @@ -8268,7 +8278,7 @@ msgstr "" #: ../../admin/config.rst:292 ../../admin/config.rst:427 msgid "The following subdirectories usually exist:" -msgstr "" +msgstr "Les répertoires suivants existent habituellement :" #: ../../admin/config.rst:294 msgid ":file:`fonts`" @@ -8346,6 +8356,8 @@ msgid "" "Provide a fully-qualified path to the Python class implementing the check " "interface." msgstr "" +"Fournir un chemin complet vers la classe Python qui implémente l'interface " +"de vérification." #: ../../admin/config.rst:343 msgid "Adjust the list of checks to include ones relevant to you." @@ -8361,11 +8373,11 @@ msgstr "" #: ../../admin/config.rst:349 msgid "You can turn off all checks:" -msgstr "" +msgstr "Vous pouvez désactiver tous les contrôles :" #: ../../admin/config.rst:355 msgid "You can turn on only a few:" -msgstr "" +msgstr "Vous pouvez en activer que quelques uns :" #: ../../admin/config.rst:367 msgid "" @@ -8387,6 +8399,8 @@ msgid "" "Delete comments after a given number of days. Defaults to ``None``, meaning " "no deletion at all." msgstr "" +"Supprimer les commentaires passé un certain nombre de jours. La valeur par " +"défaut ``None`` signifie aucune suppression." #: ../../admin/config.rst:386 msgid "COMMIT_PENDING_HOURS" @@ -8463,7 +8477,7 @@ msgstr ":file:`home`" #: ../../admin/config.rst:430 msgid "Home directory used for invoking scripts." -msgstr "" +msgstr "Répertoire racine utilisé pour appeler les scripts." #: ../../admin/config.rst:431 msgid ":file:`ssh`" @@ -8528,6 +8542,8 @@ msgid "" "The easiest way to achieve this is to make the user the owner of the " "directory:" msgstr "" +"Le moyen le plus facile de réaliser cela est de déclarer l'utilisateur en " +"tant que propriétaire du répertoire :" #: ../../admin/config.rst:457 msgid "" @@ -8549,6 +8565,8 @@ msgid "" "Whether the database backups should be stored as plain text, compressed or " "skipped. The authorized values are:" msgstr "" +"Indique si les sauvegardes de la base de données doivent être faites sous le " +"format texte à plat ou compressé ou ignorées. Les valeurs permises sont :" #: ../../admin/config.rst:473 msgid "``\"plain\"``" @@ -8620,7 +8638,7 @@ msgstr "DEFAULT_RESTRICTED_COMPONENT" #: ../../admin/config.rst:526 msgid "The default value for component restriction." -msgstr "" +msgstr "Valeur par défaut pour la restriction du composant." #: ../../admin/config.rst:530 #, fuzzy @@ -8656,7 +8674,7 @@ msgstr "" #: ../../admin/config.rst:561 msgid "This setting affects only newly created components." -msgstr "" +msgstr "Ce paramètre ne concerne que les composants nouvellement créés." #: ../../admin/config.rst:563 ../../admin/config.rst:1082 #: ../../admin/config.rst:1325 ../../admin/config.rst:1524 @@ -8728,7 +8746,7 @@ msgstr ":ref:`component-merge_style` pour tout nouveau composant." #: ../../admin/config.rst:632 msgid "`rebase` - default" -msgstr "" +msgstr "`rebase` - par défaut" #: ../../admin/config.rst:633 msgid "`merge`" @@ -8755,6 +8773,8 @@ msgstr "DEFAULT_TRANSLATION_PROPAGATION" #: ../../admin/config.rst:652 msgid "Default setting for translation propagation, defaults to ``True``." msgstr "" +"Paramètre par défaut pour la propagation de la traduction, par défaut " +"``True``." #: ../../admin/config.rst:656 msgid ":ref:`component`, :ref:`component-allow_translation_propagation`" From 1f8f4d913225d3bafa1e8fd329af489755ff1db3 Mon Sep 17 00:00:00 2001 From: amano Date: Sun, 29 Sep 2024 13:59:57 +0000 Subject: [PATCH 04/61] Translated using Weblate (Japanese) Currently translated at 100.0% (9608 of 9608 strings) Translation: Weblate/Documentation Translate-URL: https://hosted.weblate.org/projects/weblate/documentation/ja/ --- docs/locales/ja/LC_MESSAGES/docs.po | 55 +++++++++++++++-------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/docs/locales/ja/LC_MESSAGES/docs.po b/docs/locales/ja/LC_MESSAGES/docs.po index d9387d14b00e..7f234040205d 100644 --- a/docs/locales/ja/LC_MESSAGES/docs.po +++ b/docs/locales/ja/LC_MESSAGES/docs.po @@ -12,7 +12,7 @@ msgstr "" "Project-Id-Version: Weblate 5.8\n" "Report-Msgid-Bugs-To: https://github.com/WeblateOrg/weblate/issues\n" "POT-Creation-Date: 2024-09-20 14:27+0200\n" -"PO-Revision-Date: 2024-09-29 12:15+0000\n" +"PO-Revision-Date: 2024-09-30 13:16+0000\n" "Last-Translator: amano \n" "Language-Team: Japanese \n" @@ -1158,7 +1158,7 @@ msgstr "原文情報の追加編集" #: ../../admin/access.rst:519 msgid ":guilabel:`Administration`, :guilabel:`Edit source`" -msgstr ":guilabel:`管理`, :guilabel:`原文の編集`" +msgstr ":guilabel:`管理`、:guilabel:`原文の編集`" #: ../../admin/access.rst:521 msgid "Strings" @@ -1471,9 +1471,9 @@ msgid "" msgstr "" ":guilabel:`コメントの投稿`、:guilabel:`自動提案の使用`、:guilabel:`追加の文字" "列情報を編集`、:guilabel:`提案の受入れ`、:guilabel:`提案の追加`、:guilabel:`" -"提案に投票`, :guilabel:`翻訳ファイルのダウンロード`、:guilabel:`検査不合格の" -"無視`、:guilabel:`文字列の編集`、:guilabel:`原文の編集`、:guilabel:`アップ" -"ロードで既存の文字列を上書き`、:guilabel:`翻訳のアップロード`" +"提案に投票`、:guilabel:`翻訳ファイルのダウンロード`、:guilabel:`検査不合格の" +"無視`、:guilabel:`文字列の編集`、:guilabel:`原文の編集`、:guilabel:`アップロ" +"ードで既存の文字列を上書き`、:guilabel:`翻訳のアップロード`" #: ../../admin/access.rst:614 msgid "`Add suggestion`" @@ -12644,7 +12644,7 @@ msgstr "" #: ../../admin/install.rst:873 msgid ":ref:`config`, :ref:`privileges`" -msgstr ":ref:`config`, :ref:`privileges`" +msgstr ":ref:`config`、:ref:`privileges`" #: ../../admin/install.rst:879 msgid "Production setup" @@ -12840,8 +12840,8 @@ msgid "" ":ref:`database-setup`, :ref:`database-migration`, :ref:`configuration`, :doc:" "`django:ref/databases`" msgstr "" -":ref:`database-setup`、:ref:`database-migration`、:ref:`configuration`, :doc:" -"`django:ref/databases`" +":ref:`database-setup`、:ref:`database-" +"migration`、:ref:`configuration`、:doc:`django:ref/databases`" #: ../../admin/install.rst:1022 msgid "Enable caching" @@ -13344,9 +13344,9 @@ msgid "" "compress`, :doc:`django:howto/deployment/index`, :doc:`django:howto/static-" "files/deployment`" msgstr "" -":ref:`uwsgi`, :ref:`apache`、:ref:`apache-gunicorn`、:ref:`production-" -"compress`、:doc:`django:howto/deployment/index`、:doc:`django:howto/static-" -"files/deployment`" +":ref:`uwsgi`、:ref:`apache`、:ref:`apache-gunicorn`、:ref:`production-" +"compress`、:doc:`django:howto/deployment/index`、:doc:`django:howto/" +"static-files/deployment`" #: ../../admin/install.rst:1363 msgid "Content security policy" @@ -13517,7 +13517,7 @@ msgstr "" #: ../../admin/install.rst:1478 msgid "Running :ref:`auto-translation`." -msgstr "実行 :ref:`auto-translation`." +msgstr ":ref:`auto-translation` の実行。" #: ../../admin/install.rst:1479 msgid "Sending digest notifications." @@ -16045,7 +16045,7 @@ msgid "" "volumes/>`_, :setting:`DATA_DIR`, :setting:`CACHE_DIR`" msgstr "" "`Docker ボリューム ドキュメント `_ 、:setting:`DATA_DIR`, :setting:`CACHE_DIR`" +"volumes/>`_ 、:setting:`DATA_DIR`、:setting:`CACHE_DIR`" #: ../../admin/install/docker.rst:1931 msgid "Read-only root filesystem" @@ -24226,7 +24226,7 @@ msgstr "" #: ../../api.rst:914 msgid "" "The language code to download; If not specified, all languages are included." -msgstr "ダウンロードする言語コードは、未指定の場合、全ての言語が含まれます." +msgstr "ダウンロードする言語コード。未指定の場合、全ての言語が含まれます。" #: ../../api.rst:918 msgid "" @@ -27063,7 +27063,7 @@ msgstr ":ref:`subscriptions` に更新が必要な文字列が含まれるよう #: ../../changes.rst:199 msgid "Improved compatibility with password managers." -msgstr "パスワード マネージャーとの互換性の改良." +msgstr "パスワード マネージャーとの互換性の改良。" #: ../../changes.rst:200 msgid "Improved tracking of uploaded changes." @@ -27790,7 +27790,7 @@ msgstr "" #: ../../changes.rst:518 msgid ":ref:`glossary-mt`." -msgstr ":ref:`glossary-mt`." +msgstr ":ref:`glossary-mt`。" #: ../../changes.rst:519 msgid "New automatic fixer for :ref:`autofix-punctuation-spacing`." @@ -33687,8 +33687,8 @@ msgid "" "`All changes in detail `__." msgstr "" -"`すべての変更の詳細 `__." +"`すべての変更の詳細 `__。" #: ../../changes/v4.rst:349 msgid "Weblate 4.13" @@ -42962,7 +42962,7 @@ msgstr "" #: ../../formats.rst:363 msgid "See :ref:`format-flags`." -msgstr "参照: :ref:`format-flags`." +msgstr "参照: :ref:`format-flags`。" #: ../../formats.rst:364 msgid "The gettext type comments are used as flags." @@ -45596,10 +45596,10 @@ msgid "" "`Encryption items NOT Subject to the EAR `_)." msgstr "" -"Weblate は,、ECCN 5D002 または 5D992 aに分類される可能性が高く、公開されてい" -"る Libre ソフトウェアであるため、EAR の対象にはならないはずです(参照: " -"`Encryption items NOT Subject to the EAR `_ ) 。" +"Weblate は ECCN 5D002 または 5D992 aに分類される可能性が高く、公開されている " +"Libre ソフトウェアであるため、EAR の対象にはならないはずです(参照: `" +"Encryption items NOT Subject to the EAR `_ ) " +"。" #: ../../legal.rst:41 msgid "" @@ -49148,9 +49148,10 @@ msgid "" "file, and you can choose original or converted formats similarly as in :ref:" "`download`." msgstr "" -"コンポーネント、カテゴリ、プロジェクトの翻訳ファイルは、 :guilabel:`Files` メ" -"ニューから一度にダウンロードできます。 ダウンロードは常に ZIP ファイルとして" -"提供され、:ref:`download` と同様に元の形式または変換形式を選択できます。." +"コンポーネント、カテゴリ、プロジェクトの翻訳ファイルは、 :guilabel:`ファイル`" +" メニューから一度にダウンロードできます。 ダウンロードは常に ZIP " +"ファイルとして提供され、:ref:`download` " +"と同様に元の形式か変換した形式を選択できます。" #: ../../user/files.rst:67 msgid ":http:get:`/api/components/(string:project)/(string:component)/file/`" @@ -50604,7 +50605,7 @@ msgstr "``full_name:TEXT``" #: ../../user/search.rst:192 msgid "Search in full names." -msgstr "フルネームで検索." +msgstr "フルネームで検索。" #: ../../user/search.rst:194 msgid "" From bef02128f8a2567a91d660cb7973b9632f2c9425 Mon Sep 17 00:00:00 2001 From: Hoseok Seo Date: Sun, 29 Sep 2024 12:16:19 +0000 Subject: [PATCH 05/61] Translated using Weblate (Korean) Currently translated at 18.5% (1785 of 9608 strings) Translation: Weblate/Documentation Translate-URL: https://hosted.weblate.org/projects/weblate/documentation/ko/ --- docs/locales/ko/LC_MESSAGES/docs.po | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/locales/ko/LC_MESSAGES/docs.po b/docs/locales/ko/LC_MESSAGES/docs.po index 6443c9ddadc1..2198612912fd 100644 --- a/docs/locales/ko/LC_MESSAGES/docs.po +++ b/docs/locales/ko/LC_MESSAGES/docs.po @@ -11,7 +11,7 @@ msgstr "" "Project-Id-Version: Weblate 5.8\n" "Report-Msgid-Bugs-To: https://github.com/WeblateOrg/weblate/issues\n" "POT-Creation-Date: 2024-09-20 14:27+0200\n" -"PO-Revision-Date: 2024-09-29 12:15+0000\n" +"PO-Revision-Date: 2024-09-30 13:16+0000\n" "Last-Translator: Hoseok Seo \n" "Language-Team: Korean \n" @@ -16123,7 +16123,7 @@ msgstr "응용프로그램 엔드포인트 URL" #: ../../admin/machine.rst:401 msgid "" "Endpoint URL of the instance, e.g: https://my-instance.openai.azure.com." -msgstr "" +msgstr "인스턴스의 엔드포인트 URL, 예: https://my-instance.openai.azure.com." #: ../../admin/machine.rst:403 msgid "``deployment``" @@ -16138,7 +16138,7 @@ msgstr "응용프로그램 엔드포인트 URL" #: ../../admin/machine.rst:403 msgid "The model's unique deployment name." -msgstr "" +msgstr "모델의 고유한 배포 이름입니다." #: ../../admin/machine.rst:406 #, fuzzy From 5f195f83d4587e2503511a2967c5878eff63a412 Mon Sep 17 00:00:00 2001 From: Reno Tx Date: Sun, 29 Sep 2024 19:20:31 +0000 Subject: [PATCH 06/61] Translated using Weblate (Serbian) Currently translated at 11.7% (1127 of 9608 strings) Translation: Weblate/Documentation Translate-URL: https://hosted.weblate.org/projects/weblate/documentation/sr/ --- docs/locales/sr/LC_MESSAGES/docs.po | 184 ++++++++++++++++------------ 1 file changed, 109 insertions(+), 75 deletions(-) diff --git a/docs/locales/sr/LC_MESSAGES/docs.po b/docs/locales/sr/LC_MESSAGES/docs.po index 60df0fb2e8bf..89a55aaced11 100644 --- a/docs/locales/sr/LC_MESSAGES/docs.po +++ b/docs/locales/sr/LC_MESSAGES/docs.po @@ -12,8 +12,8 @@ msgstr "" "Project-Id-Version: Weblate 5.8\n" "Report-Msgid-Bugs-To: https://github.com/WeblateOrg/weblate/issues\n" "POT-Creation-Date: 2024-09-20 14:27+0200\n" -"PO-Revision-Date: 2024-09-21 02:47+0000\n" -"Last-Translator: Fill read-only add-on \n" +"PO-Revision-Date: 2024-09-30 13:16+0000\n" +"Last-Translator: Reno Tx \n" "Language-Team: Serbian \n" "Language: sr\n" @@ -77,7 +77,7 @@ msgstr "Svima dostupno." #: ../../admin/access.rst:28 msgid "Any authenticated user can contribute." -msgstr "" +msgstr "Сваки аутентификовани корисник може допринети." #: ../../admin/access.rst:30 msgid "VCS repository might be exposed to everybody." @@ -1119,7 +1119,7 @@ msgstr "Одобрава ниске" #: ../../admin/access.rst:531 msgid "Edit string when suggestions are enforced" -msgstr "" +msgstr "Urediti ниску када су предлози обавезни" #: ../../admin/access.rst:533 msgid "Edit source strings" @@ -1257,7 +1257,7 @@ msgstr "Поништава измене у локалном складишту" #: ../../admin/access.rst:567 msgid "View upstream repository location" -msgstr "" +msgstr "Погледај локацију узводног складишта" #: ../../admin/access.rst:569 msgid "Update the internal repository" @@ -1269,7 +1269,7 @@ msgstr "" #: ../../admin/access.rst:571 msgid "Use management interface" -msgstr "" +msgstr "Користите интерфејс за управљање" #: ../../admin/access.rst:573 msgid "Add new projects" @@ -1872,7 +1872,7 @@ msgstr "``threshold``" #: ../../admin/addons.rst:60 ../../api.rst:2003 msgid "Score threshold" -msgstr "" +msgstr "Праг резултата" #: ../../admin/addons.rst:0 msgid "Triggers" @@ -1890,6 +1890,7 @@ msgid "" "Automatically translates strings using machine translation or other " "components." msgstr "" +"Аутоматски преводи низове користећи машински превод или друге компоненте." #: ../../admin/addons.rst:66 msgid "It is triggered:" @@ -1913,7 +1914,7 @@ msgstr "" #: ../../admin/addons.rst:79 msgid "JavaScript localization CDN" -msgstr "" +msgstr "JavaScript локализациони CDN" #: ../../admin/addons.rst:83 msgid "``weblate.cdn.cdnjs``" @@ -1933,11 +1934,11 @@ msgstr "``css_selector``" #: ../../admin/addons.rst:87 ../../devel/html.rst:51 msgid "CSS selector" -msgstr "" +msgstr "CSS селектор" #: ../../admin/addons.rst:87 msgid "CSS selector to detect localizable elements." -msgstr "" +msgstr "CSS селектор за детекцију локализованих елемената." #: ../../admin/addons.rst:89 msgid "``cookie_name``" @@ -1949,7 +1950,7 @@ msgstr "Назив колачића језика" #: ../../admin/addons.rst:89 msgid "Name of cookie which stores language preference." -msgstr "" +msgstr "Име колачића који чува језичке преференције." #: ../../admin/addons.rst:91 msgid "``files``" @@ -1964,6 +1965,8 @@ msgid "" "List of filenames in current repository or remote URLs to parse for " "translatable strings." msgstr "" +"Листа имена фајлова у тренутном складишту или удаљених URL-ова за парсирање " +"преводивих низова." #: ../../admin/addons.rst:93 msgid "daily, repository post-commit, repository post-update" @@ -2063,6 +2066,9 @@ msgid "" "file formats, this means removing stale translation keys no longer present " "in the base file." msgstr "" +"Ажурирајте све преводе да одговарају једнојезичном основном фајлу. За већину " +"формата фајлова, ово значи уклањање застарелих преводних кључева који више " +"нису присутни у основном фајлу." #: ../../admin/addons.rst:156 msgid "" @@ -2172,7 +2178,7 @@ msgstr "``base_file_template``" #: ../../admin/addons.rst:199 msgid "Define the monolingual base filename" -msgstr "" +msgstr "Дефиниши једнојезични основни назив фајла" #: ../../admin/addons.rst:199 msgid "Leave empty for bilingual translation files." @@ -2191,6 +2197,8 @@ msgid "" "Filename of file used for creating new translations. For gettext choose .pot " "file." msgstr "" +"Назив фајла који се користи за креирање нових превода. За gettext изаберите ." +"pot фајл." #: ../../admin/addons.rst:203 msgid "``intermediate_template``" @@ -2206,6 +2214,9 @@ msgid "" "translation file provided by developers and is used when creating actual " "source strings." msgstr "" +"Име фајла посредног преводног фајла. У већини случајева ово је преводни фајл " +"који обезбеђују програмери и користи се приликом креирања стварних изворних " +"низова." #: ../../admin/addons.rst:205 msgid "``language_regex``" @@ -2247,7 +2258,7 @@ msgstr "``confirm``" #: ../../admin/addons.rst:211 msgid "I confirm the above matches look correct" -msgstr "" +msgstr "Потврђујем да горе наведена подударања изгледају исправно" #: ../../admin/addons.rst:213 ../../admin/addons.rst:709 #: ../../admin/addons.rst:848 @@ -2261,6 +2272,8 @@ msgid "" "Automatically adds or removes project components based on file changes in " "the version control system." msgstr "" +"Аутоматски додаје или уклања компоненте пројекта на основу промена фајлова у " +"систему контроле верзија." #: ../../admin/addons.rst:218 msgid "" @@ -2576,7 +2589,7 @@ msgstr "``state``" #: ../../admin/addons.rst:349 msgid "State to set" -msgstr "" +msgstr "Стање за подешавање" #: ../../admin/addons.rst:351 #, fuzzy @@ -2624,7 +2637,7 @@ msgstr "``add_labels``" #: ../../admin/addons.rst:363 ../../admin/addons.rst:382 msgid "Labels to add" -msgstr "" +msgstr "Ознаке за додавање" #: ../../admin/addons.rst:365 msgid "``remove_labels``" @@ -2732,6 +2745,9 @@ msgid "" "needing editing in Weblate. This way you can easily filter and edit source " "strings written by the developers." msgstr "" +"Када год се нова изворна ниска увезе из VCS-а, означава се као потребна за " +"уређивање у Веблејту. На овај начин можете лако филтрирати и уредити изворне " +"ниске које су написали програмери." #: ../../admin/addons.rst:443 msgid "Flag new translations as \"Needs editing\"" @@ -2747,6 +2763,9 @@ msgid "" "as needing editing in Weblate. This way you can easily filter and edit " "translations created by the developers." msgstr "" +"Када год се нова преводива ниска увезе из VCS-а, означава се као потребна за " +"уређивање у Веблејту. На овај начин можете лако филтрирати и уредити преводе " +"које су креирали програмери." #: ../../admin/addons.rst:460 #, fuzzy @@ -3015,7 +3034,7 @@ msgstr "" #: ../../admin/addons.rst:619 msgid "Update ALL_LINGUAS variable in the \"configure\" file" -msgstr "" +msgstr "Ажурирајте ALL_LINGUAS променљиву у \"configure\" датотеци" #: ../../admin/addons.rst:621 msgid "``weblate.gettext.configure``" @@ -3035,7 +3054,7 @@ msgstr "" #: ../../admin/addons.rst:631 msgid "Customize gettext output" -msgstr "" +msgstr "Прилагоди gettext излаз" #: ../../admin/addons.rst:633 msgid "``weblate.gettext.customize``" @@ -3083,6 +3102,7 @@ msgstr "" msgid "" "Allows customization of gettext output behavior, for example line wrapping." msgstr "" +"Омогућава прилагођавање понашања gettext излаза, на пример преламање линија." #: ../../admin/addons.rst:649 msgid "It offers the following options:" @@ -3116,7 +3136,7 @@ msgstr "" #: ../../admin/addons.rst:670 msgid "Updates the LINGUAS file when a new translation is added." -msgstr "" +msgstr "Ажурира LINGUAS фајл када се дода нови превод." #: ../../admin/addons.rst:675 msgid "Generate MO files" @@ -3136,7 +3156,7 @@ msgstr "Путања направљеног МО фајла" #: ../../admin/addons.rst:679 msgid "If not specified, the location of the PO file will be used." -msgstr "" +msgstr "Ако није наведено, користиће се локација PO датотеке." #: ../../admin/addons.rst:681 ../../admin/addons.rst:707 msgid "``fuzzy``" @@ -3156,7 +3176,7 @@ msgstr "" #: ../../admin/addons.rst:685 msgid "Automatically generates a MO file for every changed PO file." -msgstr "" +msgstr "Аутоматски генерише MO фајл за сваки промењени PO фајл." #: ../../admin/addons.rst:687 msgid "" @@ -3197,7 +3217,7 @@ msgstr "Уклони локације преведених ниски" #: ../../admin/addons.rst:707 msgid "Use fuzzy matching" -msgstr "" +msgstr "Користите фази подударање" #: ../../admin/addons.rst:711 msgid "" @@ -3214,7 +3234,7 @@ msgstr "" #: ../../admin/addons.rst:725 msgid "Squash Git commits" -msgstr "" +msgstr "Споји Git комите" #: ../../admin/addons.rst:727 msgid "``weblate.git.squash``" @@ -3226,7 +3246,7 @@ msgstr "``squash``" #: ../../admin/addons.rst:729 msgid "Commit squashing" -msgstr "" +msgstr "Спајање комита" #: ../../admin/addons.rst:731 msgid "``all`` -- All commits into one" @@ -3253,7 +3273,7 @@ msgstr "``append_trailers``" #: ../../admin/addons.rst:739 msgid "Append trailers to squashed commit message" -msgstr "" +msgstr "Додајте приколице у поруку урезивања" #: ../../admin/addons.rst:739 msgid "" @@ -3277,6 +3297,8 @@ msgid "" "This commit message will be used instead of the combined commit messages " "from the squashed commits." msgstr "" +"Ова порука урезивања ће се користити уместо комбинованих порука урезивања из " +"спојених урезивања." #: ../../admin/addons.rst:743 #, fuzzy @@ -3286,7 +3308,7 @@ msgstr "Статус складишта" #: ../../admin/addons.rst:745 msgid "Squash Git commits prior to pushing changes." -msgstr "" +msgstr "Споји Git комите пре слања промена." #: ../../admin/addons.rst:747 msgid "" @@ -3296,7 +3318,7 @@ msgstr "" #: ../../admin/addons.rst:750 msgid "All commits into one" -msgstr "" +msgstr "Сви комити у један" #: ../../admin/addons.rst:751 msgid "Per language" @@ -3332,7 +3354,7 @@ msgstr "" #: ../../admin/addons.rst:768 msgid "Customize JSON output" -msgstr "" +msgstr "Прилагоди JSON излаз" #: ../../admin/addons.rst:770 msgid "``weblate.json.customize``" @@ -3377,6 +3399,7 @@ msgstr "" msgid "" "Allows adjusting JSON output behavior, for example indentation or sorting." msgstr "" +"Омогућава подешавање понашања JSON излаза, на пример увлачење или сортирање." #: ../../admin/addons.rst:789 #, fuzzy @@ -3441,7 +3464,7 @@ msgstr "``age``" #: ../../admin/addons.rst:812 ../../admin/addons.rst:829 msgid "Days to keep" -msgstr "" +msgstr "Дани за чување" #: ../../admin/addons.rst:814 ../../admin/addons.rst:833 msgid "daily" @@ -3449,7 +3472,7 @@ msgstr "" #: ../../admin/addons.rst:816 msgid "Set a timeframe for removal of comments." -msgstr "" +msgstr "Поставите временски оквир за уклањање коментара." #: ../../admin/addons.rst:818 msgid "" @@ -3478,11 +3501,11 @@ msgstr "Праг гласања" #: ../../admin/addons.rst:831 msgid "Threshold for removal. This field has no effect with voting turned off." -msgstr "" +msgstr "Праг за уклањање. Ово поље нема ефекта ако је гласање искључено." #: ../../admin/addons.rst:835 msgid "Set a timeframe for removal of suggestions." -msgstr "" +msgstr "Поставите временски оквир за уклањање сугестија." #: ../../admin/addons.rst:837 msgid "" @@ -3505,6 +3528,9 @@ msgid "" "Unused strings are removed, and new ones added as copies of the source " "string." msgstr "" +"Ажурирајте све преводне фајлове да одговарају једнојезичном узводном базном " +"фајлу. Неискоришћени низови се уклањају, а нови се додају као копије " +"изворног низа." #: ../../admin/addons.rst:855 msgid "" @@ -3536,7 +3562,7 @@ msgstr "" #: ../../admin/addons.rst:880 msgid "Customize YAML output" -msgstr "" +msgstr "Прилагодите YAML излаз" #: ../../admin/addons.rst:882 msgid "``weblate.yaml.customize``" @@ -3602,6 +3628,8 @@ msgstr "" msgid "" "Allows adjusting YAML output behavior, for example line-length or newlines." msgstr "" +"Омогућава подешавање понашања YAML излаза, на пример дужину линије или нове " +"линије." #: ../../admin/addons.rst:912 #, fuzzy @@ -3986,7 +4014,7 @@ msgstr "" #: ../../admin/announcements.rst:2 msgid "Announcements" -msgstr "" +msgstr "Обавештења" #: ../../admin/announcements.rst:6 msgid "In prior releases this feature was called whiteboard messages." @@ -18192,7 +18220,7 @@ msgstr "" #: ../../admin/projects.rst:80 msgid "Start from scratch" -msgstr "" +msgstr "Почните од почетка" #: ../../admin/projects.rst:81 msgid "Create blank translation project and add strings manually." @@ -18615,7 +18643,7 @@ msgstr "" #: ../../admin/projects.rst:413 msgid "Push branch" -msgstr "" +msgstr "Пошаљи грану" #: ../../admin/projects.rst:415 msgid "Branch for pushing changes, leave empty to use :ref:`component-branch`." @@ -18988,6 +19016,8 @@ msgid "" "User agreement which needs to be approved before a user can translate this " "component." msgstr "" +"Кориснички уговор који мора бити одобрен пре него што корисник може превести " +"ову компоненту." #: ../../admin/projects.rst:662 msgid "Adding new translation" @@ -19099,6 +19129,8 @@ msgid "" "Customize language code used to generate the filename for translations " "created by Weblate." msgstr "" +"Прилагодите код језика који се користи за генерисање имена фајла за преводе " +"које креира Веблејт." #: ../../admin/projects.rst:733 msgid "" @@ -19108,7 +19140,7 @@ msgstr "" #: ../../admin/projects.rst:736 msgid "Default based on the file format" -msgstr "" +msgstr "Подразумевано на основу формата датотеке." #: ../../admin/projects.rst:737 msgid "Dependent on file format, for most of them POSIX is used." @@ -19116,7 +19148,7 @@ msgstr "" #: ../../admin/projects.rst:738 msgid "POSIX style using underscore as a separator" -msgstr "" +msgstr "POSIX стил користи доњу црту као сепаратор" #: ../../admin/projects.rst:739 ../../admin/projects.rst:742 msgid "" @@ -19130,7 +19162,7 @@ msgstr "" #: ../../admin/projects.rst:744 msgid "POSIX style using underscore as a separator, including country code" -msgstr "" +msgstr "POSIX стил користећи доњу црту као сепаратор, укључујући код земље" #: ../../admin/projects.rst:745 msgid "" @@ -19152,7 +19184,7 @@ msgstr "" #: ../../admin/projects.rst:750 msgid "BCP style using hyphen as a separator" -msgstr "" +msgstr "BCP стил користи цртицу као сепаратор" #: ../../admin/projects.rst:751 msgid "" @@ -19161,7 +19193,7 @@ msgstr "" #: ../../admin/projects.rst:753 msgid "BCP style using hyphen as a separator, including country code" -msgstr "" +msgstr "BCP стил користећи цртицу као сепаратор, укључујући код земље" #: ../../admin/projects.rst:754 msgid "" @@ -19203,7 +19235,7 @@ msgstr "" #: ../../admin/projects.rst:764 msgid "Android style" -msgstr "" +msgstr "Android стил." #: ../../admin/projects.rst:765 msgid "Only used in Android apps, produces language codes like ``pt-rBR``." @@ -19327,7 +19359,7 @@ msgstr "" #: ../../admin/projects.rst:840 msgid "Age of changes to commit" -msgstr "" +msgstr "Старост промена за комитовање" #: ../../admin/projects.rst:842 msgid "" @@ -19349,7 +19381,7 @@ msgstr "" #: ../../admin/projects.rst:857 msgid "Lock on error" -msgstr "" +msgstr "Закључај на грешку" #: ../../admin/projects.rst:859 msgid "" @@ -19481,7 +19513,7 @@ msgstr "Приоритет" #: ../../admin/projects.rst:935 msgid "Components with higher priority are offered first to translators." -msgstr "" +msgstr "Компоненте са вишим приоритетом се прво нуде преводиоцима." #: ../../admin/projects.rst:939 msgid "This now also affects ordering of matched glossary terms." @@ -21254,7 +21286,7 @@ msgstr "" #: ../../api.rst:1969 ../../api.rst:1992 ../../api.rst:2023 ../../api.rst:2034 #: ../../api.rst:2062 ../../api.rst:2075 ../../api.rst:2088 msgid "Project URL slug" -msgstr "" +msgstr "Слаг URL-а пројекта" #: ../../api.rst:842 msgid "project name" @@ -21328,7 +21360,7 @@ msgstr "" #: ../../api.rst:1971 ../../api.rst:1994 ../../api.rst:2025 ../../api.rst:2036 #: ../../api.rst:2064 ../../api.rst:2077 ../../api.rst:2090 msgid "Component URL slug" -msgstr "" +msgstr "Слаг URL-а компоненте" #: ../../api.rst:883 msgid "Edit a project by a :http:method:`PUT` request." @@ -22280,6 +22312,8 @@ msgid "" "Turn on contribution to shared translation memory for the project to get " "access to additional components." msgstr "" +"Укључите допринос заједничкој меморији превода за пројекат да бисте добили " +"приступ додатним компонентама." #: ../../api.rst:2007 msgid "" @@ -36213,7 +36247,7 @@ msgstr "" #: ../../devel/alerts.rst:19 msgid "Misconfigured monolingual translation." -msgstr "" +msgstr "Погрешно конфигурисан једнојезични превод." #: ../../devel/alerts.rst:20 #, fuzzy @@ -36301,7 +36335,7 @@ msgstr "" #: ../../devel/community.rst:11 msgid "Community localization checklist" -msgstr "" +msgstr "Контролна листа за локализацију заједнице" #: ../../devel/community.rst:13 msgid "" @@ -37172,7 +37206,7 @@ msgstr "" #: ../../devel/reporting.rst:23 msgid "Number of words" -msgstr "" +msgstr "Број речи" #: ../../devel/reporting.rst:27 msgid "" @@ -40481,7 +40515,7 @@ msgstr "" #: ../../formats/laravel.rst:4 msgid "Laravel PHP strings" -msgstr "" +msgstr "Laravel PHP низови" #: ../../formats/laravel.rst:8 msgid "The Laravel PHP localization files are supported as well with plurals:" @@ -42467,11 +42501,11 @@ msgstr "" #: ../../user/checks.rst:478 msgid "ECMAScript template literals" -msgstr "" +msgstr "ECMAScript шаблонски литерали" #: ../../user/checks.rst:480 msgid "ECMAScript template literals do not match source" -msgstr "" +msgstr "ECMAScript шаблонски литерали не одговарају извору" #: ../../user/checks.rst:482 msgid "``weblate.checks.format.ESTemplateLiteralsCheck``" @@ -42497,11 +42531,11 @@ msgstr "" #: ../../user/checks.rst:496 msgid "i18next interpolation" -msgstr "" +msgstr "i18next интерполација" #: ../../user/checks.rst:500 msgid "The i18next interpolation does not match source" -msgstr "" +msgstr "i18next интерполација не одговара извору" #: ../../user/checks.rst:502 msgid "``weblate.checks.format.I18NextInterpolationCheck``" @@ -42742,11 +42776,11 @@ msgstr "" #: ../../user/checks.rst:638 msgid "JavaScript format" -msgstr "" +msgstr "JavaScript формат" #: ../../user/checks.rst:640 msgid "JavaScript format string does not match source" -msgstr "" +msgstr "JavaScript формат низ не одговара извору" #: ../../user/checks.rst:642 msgid "``weblate.checks.format.JavaScriptFormatCheck``" @@ -42918,7 +42952,7 @@ msgstr "Python формат са заградама" #: ../../user/checks.rst:773 msgid "Python brace format string does not match source" -msgstr "" +msgstr "Python формат са заградама не одговара извору" #: ../../user/checks.rst:775 msgid "``weblate.checks.format.PythonBraceFormatCheck``" @@ -42953,7 +42987,7 @@ msgstr "Python формат" #: ../../user/checks.rst:793 msgid "Python format string does not match source" -msgstr "" +msgstr "Python формат низ не одговара извору" #: ../../user/checks.rst:795 msgid "``weblate.checks.format.PythonFormatCheck``" @@ -43274,11 +43308,11 @@ msgstr "" #: ../../user/checks.rst:985 msgid "Markdown links" -msgstr "" +msgstr "Markdown везе" #: ../../user/checks.rst:987 msgid "Markdown links do not match source" -msgstr "" +msgstr "Markdown везе не одговарају извору" #: ../../user/checks.rst:989 msgid "``weblate.checks.markup.MarkdownLinkCheck``" @@ -43302,7 +43336,7 @@ msgstr "Маркдаун смернице" #: ../../user/checks.rst:1006 msgid "Markdown link references do not match source" -msgstr "" +msgstr "Markdown референце линкова се не подударају са извором" #: ../../user/checks.rst:1008 msgid "``weblate.checks.markup.MarkdownRefLinkCheck``" @@ -43322,11 +43356,11 @@ msgstr "" #: ../../user/checks.rst:1022 msgid "Markdown syntax" -msgstr "" +msgstr "Markdown синтакса" #: ../../user/checks.rst:1024 ../../user/checks.rst:1031 msgid "Markdown syntax does not match source" -msgstr "" +msgstr "Markdown синтакса не одговара извору" #: ../../user/checks.rst:1026 msgid "``weblate.checks.markup.MarkdownSyntaxCheck``" @@ -43727,7 +43761,7 @@ msgstr "" #: ../../user/checks.rst:1292 msgid "Placeholders" -msgstr "" +msgstr "Замене" #: ../../user/checks.rst:1294 msgid "Translation is missing some placeholders" @@ -43774,7 +43808,7 @@ msgstr ":ref:`custom-checks`" #: ../../user/checks.rst:1338 msgid "Missing non breakable space before double punctuation sign" -msgstr "" +msgstr "Недостаје непрекидна размака пре знака двоструке интерпункције" #: ../../user/checks.rst:1340 msgid "``weblate.checks.chars.PunctuationSpacingCheck``" @@ -44013,7 +44047,7 @@ msgstr "" #: ../../user/checks.rst:1508 msgid "Unsafe HTML" -msgstr "" +msgstr "Небезбедан HTML" #: ../../user/checks.rst:1510 msgid "The translation uses unsafe HTML markup" @@ -44509,7 +44543,7 @@ msgstr "Дуго непреведено" #: ../../user/checks.rst:1774 msgid "The string has not been translated for a long time" -msgstr "" +msgstr "Ниска није преведена дуго времена" #: ../../user/checks.rst:1776 msgid "``weblate.checks.source.LongUntranslatedCheck``" @@ -44557,13 +44591,15 @@ msgstr "" #: ../../user/checks.rst:1806 msgid "Multiple unnamed variables" -msgstr "" +msgstr "Више неименованих варијабли" #: ../../user/checks.rst:1810 msgid "" "There are multiple unnamed variables in the string, making it impossible for " "translators to reorder them" msgstr "" +"Постоји више неименованих варијабли у низу, што онемогућава преводиоцима да " +"их преуреде" #: ../../user/checks.rst:1812 msgid "``weblate.checks.format.MultipleUnnamedFormatsCheck``" @@ -44590,7 +44626,7 @@ msgstr "Без множине" #: ../../user/checks.rst:1826 msgid "The string is used as plural, but not using plural forms" -msgstr "" +msgstr "Ниска се користи као множина, али не користи облике множине" #: ../../user/checks.rst:1828 msgid "``weblate.checks.source.OptionalPluralCheck``" @@ -46266,7 +46302,7 @@ msgstr "" #: ../../user/translating.rst:95 msgid "Labels" -msgstr "" +msgstr "Ознаке" #: ../../user/translating.rst:97 msgid "" @@ -47320,7 +47356,7 @@ msgstr "" #: ../../vcs.rst:275 msgid "Git with force push" -msgstr "" +msgstr "Git са force push" #: ../../vcs.rst:277 msgid "" @@ -48070,10 +48106,8 @@ msgid "" msgstr "" #: ../../workflows.rst:16 -#, fuzzy -#| msgid "Reset customization" msgid "Workflow customization" -msgstr "Поништи прилагођавање" +msgstr "Прилагођавање тока рада" #: ../../workflows.rst:18 msgid "" @@ -48261,7 +48295,7 @@ msgstr "Белешка" #: ../../workflows.rst:112 ../../workflows.rst:117 ../../workflows.rst:144 #: ../../workflows.rst:148 ../../workflows.rst:179 ../../workflows.rst:182 msgid "off" -msgstr "" +msgstr "искључено" #: ../../workflows.rst:112 ../../workflows.rst:144 ../../workflows.rst:177 msgid "Configured at project level." From ba97432f07dee64a7db4a0314647f332f742d7f1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:19:28 +0000 Subject: [PATCH 07/61] chore(deps): update actions/checkout action to v4 --- .github/workflows/fossa.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/fossa.yml b/.github/workflows/fossa.yml index bc66330388cc..9635af01cd85 100644 --- a/.github/workflows/fossa.yml +++ b/.github/workflows/fossa.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Run FOSSA Scan uses: fossas/fossa-action@v1.4.0 From 0767caf67ae38626bd28017f6543c3d50d3cb480 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:19:33 +0000 Subject: [PATCH 08/61] chore(deps): update postgres docker tag to v17 --- ci/docker-compose-postgresql.yml | 2 +- dev-docker/docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/docker-compose-postgresql.yml b/ci/docker-compose-postgresql.yml index 88b8b476d05b..a9f88250f5b1 100644 --- a/ci/docker-compose-postgresql.yml +++ b/ci/docker-compose-postgresql.yml @@ -4,7 +4,7 @@ services: database: - image: postgres:16.4 + image: postgres:17.0 ports: - 60000:5432 # make postgres faster and non-durable, see https://www.postgresql.org/docs/current/non-durability.html diff --git a/dev-docker/docker-compose.yml b/dev-docker/docker-compose.yml index 80f93095f2dc..59951e9fe072 100644 --- a/dev-docker/docker-compose.yml +++ b/dev-docker/docker-compose.yml @@ -42,7 +42,7 @@ services: env_file: - ./environment database: - image: postgres:16-alpine + image: postgres:17-alpine env_file: - ./environment volumes: From 320fd7f9a8085a223dc67feee76f3733f50e382e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:19:23 +0000 Subject: [PATCH 09/61] chore(deps): update github/codeql-action action to v3.26.10 --- .github/workflows/scorecards.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index f1b8123b1512..f877b9c43cec 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -61,6 +61,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@v3.26.9 + uses: github/codeql-action/upload-sarif@v3.26.10 with: sarif_file: results.sarif From cb71e11c28785db493e41ddb430c264bce86452d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Mon, 30 Sep 2024 14:10:32 +0200 Subject: [PATCH 10/61] fix(billing): update billing status at the end of removal transaction This way it gets complete view and not something intermediate while deleting a lot of translations, components or whole project. Fixes #12627 Fixes WEBLATE-Y Fixes WEBLATE-VHT Fixes #12477 --- weblate/billing/models.py | 27 +++++++++++++++++++-------- weblate/billing/tasks.py | 7 +++++-- weblate/billing/tests.py | 5 ++--- weblate/trans/models/project.py | 3 +++ 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/weblate/billing/models.py b/weblate/billing/models.py index 0cebd4ebc445..4bc9f5df5692 100644 --- a/weblate/billing/models.py +++ b/weblate/billing/models.py @@ -7,13 +7,14 @@ import os.path from contextlib import suppress from datetime import timedelta +from functools import partial from appconf import AppConf from django.conf import settings from django.contrib import admin from django.core.exceptions import ValidationError from django.core.serializers.json import DjangoJSONEncoder -from django.db import models +from django.db import models, transaction from django.db.models import Prefetch, Q from django.db.models.signals import m2m_changed, post_delete, post_save, pre_delete from django.dispatch import receiver @@ -599,27 +600,37 @@ def update_project_bill(sender, instance, **kwargs) -> None: @receiver(pre_delete, sender=Component) @receiver(post_delete, sender=Translation) @disable_for_loaddata -def record_project_bill(sender, instance, **kwargs) -> None: +def record_project_bill( + sender, instance: Project | Component | Translation, **kwargs +) -> None: if isinstance(instance, Translation): instance = instance.component if isinstance(instance, Component): instance = instance.project - # Track billings to update for delete_project_bill - instance.billings_to_update = list(instance.billing_set.all()) + # Collect billings to update for delete_project_bill + instance.billings_to_update = list( + instance.billing_set.values_list("pk", flat=True) + ) @receiver(post_delete, sender=Project) @receiver(post_delete, sender=Component) @receiver(post_delete, sender=Translation) @disable_for_loaddata -def delete_project_bill(sender, instance, **kwargs) -> None: +def delete_project_bill( + sender, instance: Project | Component | Translation, **kwargs +) -> None: + from weblate.billing.tasks import billing_check + if isinstance(instance, Translation): instance = instance.component if isinstance(instance, Component): instance = instance.project - # This is set in record_project_bill - for billing in instance.billings_to_update: - billing.check_limits() + # This is collected in record_project_bill + for billing_id in instance.billings_to_update: + transaction.on_commit(partial(billing_check, billing_id)) + # Clear the list to avoid repeated trigger + instance.billings_to_update.clear() @receiver(post_save, sender=Invoice) diff --git a/weblate/billing/tasks.py b/weblate/billing/tasks.py index 43c1a25724db..75993dd80ee2 100644 --- a/weblate/billing/tasks.py +++ b/weblate/billing/tasks.py @@ -17,8 +17,11 @@ @app.task(trail=False) -def billing_check() -> None: - Billing.objects.check_limits() +def billing_check(billing_id: int | None = None) -> None: + if billing_id is None: + Billing.objects.check_limits() + else: + Billing.objects.get(pk=billing_id).check_limits() @app.task(trail=False) diff --git a/weblate/billing/tests.py b/weblate/billing/tests.py index 2fecec7d3e0e..8dfc27d681b2 100644 --- a/weblate/billing/tests.py +++ b/weblate/billing/tests.py @@ -9,7 +9,6 @@ from django.core import mail from django.core.exceptions import ValidationError from django.core.management import call_command -from django.test import TestCase from django.test.utils import override_settings from django.urls import reverse from django.utils import timezone @@ -24,13 +23,13 @@ schedule_removal, ) from weblate.trans.models import Project -from weblate.trans.tests.test_models import RepoTestCase +from weblate.trans.tests.test_models import BaseTestCase, RepoTestCase from weblate.trans.tests.utils import create_test_billing TEST_DATA = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test-data") -class BillingTest(TestCase): +class BillingTest(BaseTestCase): def setUp(self) -> None: self.user = User.objects.create_user( username="bill", password="kill", email="noreply@example.net" diff --git a/weblate/trans/models/project.py b/weblate/trans/models/project.py index a64ea440f0e8..28f158e60167 100644 --- a/weblate/trans/models/project.py +++ b/weblate/trans/models/project.py @@ -235,6 +235,9 @@ class Project(models.Model, PathMixin, CacheKeyMixin): objects = ProjectQuerySet.as_manager() + # Used when updating for object removal + billings_to_update: list[int] + class Meta: app_label = "trans" verbose_name = "Project" From 417c6630a6829bcd1768dfb01335e363cdafca3a Mon Sep 17 00:00:00 2001 From: amano Date: Mon, 30 Sep 2024 13:58:29 +0000 Subject: [PATCH 11/61] Translated using Weblate (Japanese) Currently translated at 100.0% (9608 of 9608 strings) Translation: Weblate/Documentation Translate-URL: https://hosted.weblate.org/projects/weblate/documentation/ja/ --- docs/locales/ja/LC_MESSAGES/docs.po | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/locales/ja/LC_MESSAGES/docs.po b/docs/locales/ja/LC_MESSAGES/docs.po index 7f234040205d..c2cb97cbc99a 100644 --- a/docs/locales/ja/LC_MESSAGES/docs.po +++ b/docs/locales/ja/LC_MESSAGES/docs.po @@ -12,7 +12,7 @@ msgstr "" "Project-Id-Version: Weblate 5.8\n" "Report-Msgid-Bugs-To: https://github.com/WeblateOrg/weblate/issues\n" "POT-Creation-Date: 2024-09-20 14:27+0200\n" -"PO-Revision-Date: 2024-09-30 13:16+0000\n" +"PO-Revision-Date: 2024-09-30 17:05+0000\n" "Last-Translator: amano \n" "Language-Team: Japanese \n" @@ -12559,9 +12559,9 @@ msgid "" "show backtraces in case of error to users, when you disable it, errors will " "be sent per e-mail to ``ADMINS`` (see above)." msgstr "" -"すべての運用環境サーバーで無効化します。デバッグ モードを有効にすると、" -"Django はユーザーにエラーが発生した場合にバックトレースを表示します。無効にす" -"ると、エラーはメールごとに ``ADMINS`` (上記参照)に送信されます。" +"本番サーバーでは無効にしてください。デバッグ モードを有効にすると、Django " +"はエラーが発生したときユーザーにバックトレースを表示します。無効にすると、" +"エラーはメールで ``ADMINS`` (上記参照)に送信されます。" #: ../../admin/install.rst:818 msgid "" @@ -12876,9 +12876,8 @@ msgid "" "In addition to caching of Django, Weblate performs caching of avatars. It is " "recommended to use a separate, file-backed cache for this purpose:" msgstr "" -"Django のキャッシュに加えて、Weblate はアバターのキャッシュも行います。そのた" -"め、別々のファイル バックアップ キャッシュにすることが望ましいです。別々の" -"キャッシュの設定例:" +"Django のキャッシュに加えて、Weblate はアバターのキャッシュも行います。そちら" +"は、独立したファイルベースのキャッシュにすることをおすすめします:" #: ../../admin/install.rst:1085 msgid "" From 34a011de141cb72210996fdb5c97837711afbda7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 07:51:42 +0200 Subject: [PATCH 12/61] fix(templates): pass user to show_info It needs it for permission checks. --- weblate/trans/templatetags/translations.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/weblate/trans/templatetags/translations.py b/weblate/trans/templatetags/translations.py index de6f6a7aeed5..df2ab1166e83 100644 --- a/weblate/trans/templatetags/translations.py +++ b/weblate/trans/templatetags/translations.py @@ -1493,8 +1493,9 @@ def list_objects_percent( ) -@register.inclusion_tag("snippets/info.html") +@register.inclusion_tag("snippets/info.html", takes_context=True) def show_info( + context, *, project: Project | None = None, component: Component | None = None, @@ -1512,6 +1513,7 @@ def show_info( This merely exists to be able to pass default values to {% include %}. """ return { + "user": context["user"], "project": project, "component": component, "translation": translation, From 47d35747d6ebdae7faa53d0aac71977b9208ff4b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 04:03:22 +0000 Subject: [PATCH 13/61] chore(deps): update dependency boto3-stubs to v1.35.30 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 42ca34f4005f..976ea5ec6a57 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -144,7 +144,7 @@ mercurial = [ "mercurial>=6.8.0,<7.0" ] mypy = [ - "boto3-stubs==1.35.29", + "boto3-stubs==1.35.30", "celery-types==0.22.0", "django-stubs-ext==5.1.0", "django-stubs==5.1.0", From 7ca2a89ee72c13d5929949e41fc93039121e25e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 08:15:02 +0200 Subject: [PATCH 14/61] fix(settings): silence drf_spectacular checks These are expected for now, the schema is currently known to be incomplete. --- weblate/settings_docker.py | 5 ++++- weblate/settings_example.py | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/weblate/settings_docker.py b/weblate/settings_docker.py index d850e46bd69b..d3c24d32c46d 100644 --- a/weblate/settings_docker.py +++ b/weblate/settings_docker.py @@ -1289,7 +1289,10 @@ SILENCED_SYSTEM_CHECKS = [ # We have modified django.contrib.auth.middleware.AuthenticationMiddleware # as weblate.accounts.middleware.AuthenticationMiddleware - "admin.E408" + "admin.E408", + # Silence drf_spectacular until these are addressed + "drf_spectacular.W001", + "drf_spectacular.W002", ] # Silence WebAuthn origin error diff --git a/weblate/settings_example.py b/weblate/settings_example.py index a2d4441f0f3e..c160f2817a25 100644 --- a/weblate/settings_example.py +++ b/weblate/settings_example.py @@ -874,7 +874,10 @@ SILENCED_SYSTEM_CHECKS = [ # We have modified django.contrib.auth.middleware.AuthenticationMiddleware # as weblate.accounts.middleware.AuthenticationMiddleware - "admin.E408" + "admin.E408", + # Silence drf_spectacular until these are addressed + "drf_spectacular.W001", + "drf_spectacular.W002", ] # Celery worker configuration for testing From ce75a070392e700ee4606d23553ca19c9033b6d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 08:35:18 +0200 Subject: [PATCH 15/61] chore(unit): add type annotations --- weblate/trans/models/unit.py | 51 +++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/weblate/trans/models/unit.py b/weblate/trans/models/unit.py index e944fdbaa4f6..a11d7fbe9707 100644 --- a/weblate/trans/models/unit.py +++ b/weblate/trans/models/unit.py @@ -55,6 +55,7 @@ if TYPE_CHECKING: from collections.abc import Generator, Iterable + from datetime import datetime from weblate.auth.models import User from weblate.machinery.base import UnitMemoryResultDict @@ -564,33 +565,33 @@ def store_old_unit(self, unit) -> None: } @property - def approved(self): + def approved(self) -> bool: return self.state == STATE_APPROVED @property - def translated(self): + def translated(self) -> bool: return self.state >= STATE_TRANSLATED @property - def readonly(self): + def readonly(self) -> bool: return self.state == STATE_READONLY @property - def fuzzy(self): + def fuzzy(self) -> bool: return self.state == STATE_FUZZY @property - def has_failing_check(self): + def has_failing_check(self) -> bool: return bool(self.active_checks) @property - def has_comment(self): + def has_comment(self) -> bool: # Use bool here as unresolved_comments might be list # or a queryset (from prefetch) return bool(self.unresolved_comments) @property - def has_suggestion(self): + def has_suggestion(self) -> bool: return bool(self.suggestions) def source_unit_save(self) -> None: @@ -1067,7 +1068,7 @@ def propagate(self, user: User, change_action=None, author=None, request=None): result = True return result - def commit_if_pending(self, author) -> None: + def commit_if_pending(self, author: User) -> None: """Commit possible previous changes on this unit.""" if self.pending: change_author = self.get_last_content_change()[0] @@ -1168,7 +1169,9 @@ def save_backend( return True - def update_source_units(self, previous_source, user: User, author) -> None: + def update_source_units( + self, previous_source: str, user: User, author: User | None + ) -> None: """ Update source for units within same component. @@ -1229,7 +1232,7 @@ def generate_change( save: bool = True, old: str | None = None, target: str | None = None, - ): + ) -> Change: """Create Change entry for saving unit.""" # Notify about new contributor if ( @@ -1279,12 +1282,12 @@ def generate_change( return change @cached_property - def suggestions(self): + def suggestions(self) -> models.QuerySet[Suggestion]: """Return all suggestions for this unit.""" return self.suggestion_set.order() @cached_property - def all_checks(self): + def all_checks(self) -> models.QuerySet[Check]: result = self.check_set.all() # Force fetching list(result) @@ -1295,20 +1298,20 @@ def clear_checks_cache(self) -> None: del self.__dict__["all_checks"] @property - def all_checks_names(self): + def all_checks_names(self) -> set[str]: return {check.name for check in self.all_checks} @property - def dismissed_checks(self): + def dismissed_checks(self) -> list[Check]: return [check for check in self.all_checks if check.dismissed] @property - def active_checks(self): + def active_checks(self) -> list[Check]: """Return all active (not ignored) checks for this unit.""" return [check for check in self.all_checks if not check.dismissed] @cached_property - def all_comments(self): + def all_comments(self) -> models.QuerySet[Comment]: """Return list of target comments.""" if self.is_source: # Add all comments on translation on source string comment @@ -1319,7 +1322,7 @@ def all_comments(self): return Comment.objects.filter(query).prefetch_related("unit", "user").order() @cached_property - def unresolved_comments(self): + def unresolved_comments(self) -> list[Comment]: return [ comment for comment in self.all_comments @@ -1423,7 +1426,7 @@ def run_checks(self, propagate: bool | None = None) -> None: # noqa: C901 if not self.is_batch_update and (create or old_checks): self.translation.invalidate_cache() - def nearby(self, count: int): + def nearby(self, count: int) -> models.QuerySet[Unit]: """Return list of nearby messages based on location.""" if self.position == 0: return Unit.objects.none() @@ -1442,7 +1445,7 @@ def nearby(self, count: int): list(result) return result - def nearby_keys(self, count): + def nearby_keys(self, count: int) -> Iterable[Unit]: # Do not show nearby keys on bilingual if not self.translation.component.has_template(): return [] @@ -1462,7 +1465,7 @@ def nearby_keys(self, count): list(result) return result - def variants(self): + def variants(self) -> Iterable[Unit]: if not self.variant: return [] return ( @@ -1482,7 +1485,7 @@ def translate( author=None, request=None, add_alternative: bool = False, - ): + ) -> bool: """ Store new translation of a unit. @@ -1670,11 +1673,11 @@ def get_max_length(self): return fallback - def get_target_hash(self): + def get_target_hash(self) -> int: return calculate_hash(self.target) @cached_property - def content_hash(self): + def content_hash(self) -> int: return calculate_hash(self.source, self.context) @cached_property @@ -1686,7 +1689,7 @@ def recent_content_changes(self): """ return self.change_set.content().select_related("author").order_by("-timestamp") - def get_last_content_change(self, silent=False): + def get_last_content_change(self, silent: bool = False) -> tuple[User, datetime]: """ Get last content change metadata. From 5a85984dde72f2ef41db0df2e8e8c2e03a00027a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 09:18:32 +0200 Subject: [PATCH 16/61] chore(deps): update scripts/spdx-license-list digest to bb7f4fc (#12640) * chore(deps): update scripts/spdx-license-list digest to bb7f4fc * utils: Update SPDX license data --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] --- scripts/spdx-license-list | 2 +- weblate/utils/licensedata.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/spdx-license-list b/scripts/spdx-license-list index ab27a721cca0..bb7f4fcaa938 160000 --- a/scripts/spdx-license-list +++ b/scripts/spdx-license-list @@ -1 +1 @@ -Subproject commit ab27a721cca0f55b37a57bf70f238454600f67e6 +Subproject commit bb7f4fcaa9388dbd071536fba7a90787c02213c8 diff --git a/weblate/utils/licensedata.py b/weblate/utils/licensedata.py index 54d43e31a3fc..75c763fd62d2 100644 --- a/weblate/utils/licensedata.py +++ b/weblate/utils/licensedata.py @@ -289,6 +289,12 @@ "https://spdx.org/licenses/Boehm-GC.html", False, ), + ( + "Boehm-GC-without-fee", + "Boehm-Demers-Weiser GC License (without fee)", + "https://spdx.org/licenses/Boehm-GC-without-fee.html", + False, + ), ( "BSL-1.0", "Boost Software License 1.0", From 453fca68d30cb3271ace4db637d0d58200aa6e9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 07:53:24 +0200 Subject: [PATCH 17/61] fix(permissions): raise error if user is missing in context Silently ignoring this makes it easy to miss user in some inclusion template tags and the permission check then silently returns false. --- weblate/auth/templatetags/permissions.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/weblate/auth/templatetags/permissions.py b/weblate/auth/templatetags/permissions.py index 4adee1cf9f69..9068a24e3e01 100644 --- a/weblate/auth/templatetags/permissions.py +++ b/weblate/auth/templatetags/permissions.py @@ -11,6 +11,8 @@ def perm(context, permission, obj=None): try: user = context["user"] - except KeyError: - return False + except KeyError as error: + raise ValueError( + "Missing user in context, could not perform permission check" + ) from error return user.has_perm(permission, obj) From 0624c3228047ed988349301d56fbc46db5b1edac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 09:42:18 +0200 Subject: [PATCH 18/61] docs: fix chapter titles --- docs/contributing/frontend.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/contributing/frontend.rst b/docs/contributing/frontend.rst index 500621b7f3d9..ca3259078c7a 100644 --- a/docs/contributing/frontend.rst +++ b/docs/contributing/frontend.rst @@ -32,8 +32,8 @@ Before proceeding with an installation, make sure you have the following prerequ - Run ``cd client``. - Run ``yarn install`` -1- Installation ---------------- +Installation +++++++++++++ To install a library, 1st run the following command: @@ -41,8 +41,8 @@ To install a library, 1st run the following command: yarn add -2- Importing the Library -------------------------- +Importing the Library ++++++++++++++++++++++ Then, there are two ways to import the library: @@ -58,8 +58,8 @@ Then, there are two ways to import the library: Note: Replace ```` with the actual name of the 3rd party library. -3- Building the Library ------------------------ +Building the Library +++++++++++++++++++++ Build the libraries used by the project, by running the following command: @@ -67,8 +67,8 @@ Build the libraries used by the project, by running the following command: yarn build -4- Including the Library ------------------------- +Including the Library ++++++++++++++++++++++ Now the library is built and ready for use. To include it follow these steps: From e6753be454594ae77fcaad012f721b72a80d3bc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 09:49:43 +0200 Subject: [PATCH 19/61] chore: fix URLs documentation --- weblate/urls.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/weblate/urls.py b/weblate/urls.py index 29a14d75b677..eff4d21c6496 100644 --- a/weblate/urls.py +++ b/weblate/urls.py @@ -815,13 +815,13 @@ weblate.wladmin.views.performance, name="manage-performance", ), - # Auth + # Accounts path("accounts/", include(weblate.accounts.urls)), # Auth path("api/", include((weblate.api.urls, "weblate.api"), namespace="api")), - # YOUR PATTERNS + # OpenAPI schema path("api/schema/", SpectacularAPIView.as_view(), name="schema"), - # Optional UI: + # API documentation path( "api/schema/swagger-ui/", SpectacularSwaggerView.as_view(url_name="schema"), From 5cc41c96aa5481ec572b2177e0ef4516f7af9ee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 08:10:01 +0200 Subject: [PATCH 20/61] fix(translation): move completion detection from task Doing that in task makes it subject to race condition - if translation is completed before previous tasks are handled, the completed event is fired multiple times. Doing the check at the end of transaction makes it both faster and more reliable. Fixes #12638 --- weblate/trans/models/translation.py | 18 ++++++++++++++++++ weblate/trans/models/unit.py | 11 ++++++++--- weblate/trans/tasks.py | 22 ---------------------- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/weblate/trans/models/translation.py b/weblate/trans/models/translation.py index da05cba4238a..1912883f19a9 100644 --- a/weblate/trans/models/translation.py +++ b/weblate/trans/models/translation.py @@ -1318,6 +1318,24 @@ def invalidate_cache(self) -> None: self._invalidate_scheduled = True transaction.on_commit(self._invalidate_triger) + def detect_completed_translation(self, change: Change, old_translated: int) -> None: + translated = self.stats.translated + if old_translated < translated and translated == self.stats.all: + self.change_set.create( + action=Change.ACTION_COMPLETE, + user=change.user, + author=change.author, + ) + + # check if component is fully translated + component = self.component + if component.stats.translated == component.stats.all: + self.component.change_set.create( + action=Change.ACTION_COMPLETED_COMPONENT, + user=change.user, + author=change.author, + ) + @property def keys_cache_key(self) -> str: return f"translation-keys-{self.pk}" diff --git a/weblate/trans/models/unit.py b/weblate/trans/models/unit.py index a11d7fbe9707..6206c0a1b561 100644 --- a/weblate/trans/models/unit.py +++ b/weblate/trans/models/unit.py @@ -5,6 +5,7 @@ from __future__ import annotations import re +from functools import partial from typing import TYPE_CHECKING, Any, TypedDict import sentry_sdk @@ -1095,8 +1096,6 @@ def save_backend( This should be always called in a transaction with updated unit locked for update. """ - from weblate.trans.tasks import detect_completed_translation - # For case when authorship specified, use user author = author or user @@ -1158,7 +1157,13 @@ def save_backend( self.translation.invalidate_cache() # Postpone completed translation detection - detect_completed_translation.delay_on_commit(change.pk, old_translated) + transaction.on_commit( + partial( + self.translation.detect_completed_translation, + change, + old_translated, + ) + ) # Update user stats change.author.profile.increase_count("translated") diff --git a/weblate/trans/tasks.py b/weblate/trans/tasks.py index 40517f5b3519..455c59af0b30 100644 --- a/weblate/trans/tasks.py +++ b/weblate/trans/tasks.py @@ -692,28 +692,6 @@ def cleanup_project_backup_download() -> None: staticfiles_storage.delete(full_name) -@app.task(trail=False) -def detect_completed_translation(change_id: int, old_translated: int) -> None: - change = Change.objects.get(pk=change_id) - - translated = change.translation.stats.translated - if old_translated < translated and translated == change.translation.stats.all: - change.translation.change_set.create( - action=Change.ACTION_COMPLETE, - user=change.user, - author=change.author, - ) - - # check if component is fully translated - component = change.translation.component - if component.stats.translated == component.stats.all: - change.translation.component.change_set.create( - action=Change.ACTION_COMPLETED_COMPONENT, - user=change.user, - author=change.author, - ) - - @app.on_after_finalize.connect def setup_periodic_tasks(sender, **kwargs) -> None: sender.add_periodic_task(3600, commit_pending.s(), name="commit-pending") From 01eec0feb881e83efc9e33619bba7fb781de8659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 10:04:14 +0200 Subject: [PATCH 21/61] chore(client): gracefully deal with null licenseText --- client/webpack.config.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/client/webpack.config.js b/client/webpack.config.js index 2c5a184f9137..b9e037242376 100644 --- a/client/webpack.config.js +++ b/client/webpack.config.js @@ -10,9 +10,11 @@ const copyrightRegex = /Copyright.*\n/; // REUSE-IgnoreStart function extractCopyright(pkg) { - const copyrights = pkg.licenseText.match(copyrightRegex); - if (copyrights !== null) { - return copyrights.join(""); + if (pkg.licenseText !== null) { + const copyrights = pkg.licenseText.match(copyrightRegex); + if (copyrights !== null) { + return copyrights.join(""); + } } return `Copyright ${pkg.author}\n`; } From a9b7d54eaf2967b53c6c41d729df06fe845b8c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 10:05:37 +0200 Subject: [PATCH 22/61] chore(client): correctly sort dependencies --- client/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/package.json b/client/package.json index 90a9d5ad45ab..c97a48a4a277 100644 --- a/client/package.json +++ b/client/package.json @@ -7,8 +7,8 @@ "build": "webpack" }, "dependencies": { - "jquery": "3.7.1", - "@sentry/browser": "8.32.0" + "@sentry/browser": "8.32.0", + "jquery": "3.7.1" }, "devDependencies": { "terser-webpack-plugin": "5.3.10", From dd82f40de4e86c34fb9b1418e772533653aea4e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 09:51:40 +0200 Subject: [PATCH 23/61] feat(api): use redoc only for documentation It looks nicer than Swagger, so let's focus on single tool only. --- docs/api.rst | 3 +-- docs/changes.rst | 3 +++ weblate/settings_example.py | 2 -- weblate/urls.py | 17 ++--------------- 4 files changed, 6 insertions(+), 19 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index ec3f3107306a..f6a2fc6e807a 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -12,8 +12,7 @@ The API is accessible on the ``/api/`` URL and it is based on You can use it directly or by :ref:`wlc`. The API is also documented using OpenAPI 3.0 on the ``/api/schema/`` URL, you -can browse it using Swagger at ``/api/schema/swagger-ui/`` or Redoc at -``/api/schema/redoc/``. +can browse at ``/api/docs/``. .. note:: diff --git a/docs/changes.rst b/docs/changes.rst index ebf4bf90e211..67bc574eec52 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -32,6 +32,9 @@ Not yet released. Please follow :ref:`generic-upgrade-instructions` in order to perform update. +* +* There are several changes in :file:`settings_example.py`, most notable are the new settings for :ref:`api` in ``SPECTACULAR_SETTINGS`` and changes in ``REST_FRAMEWORK`` and ``INSTALLED_APPS``; please adjust your settings accordingly. + **Contributors** .. include:: changes/contributors/5.8.rst diff --git a/weblate/settings_example.py b/weblate/settings_example.py index c160f2817a25..5e170dcdbd5f 100644 --- a/weblate/settings_example.py +++ b/weblate/settings_example.py @@ -828,8 +828,6 @@ "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", } SPECTACULAR_SETTINGS = { - "SWAGGER_UI_DIST": "SIDECAR", - "SWAGGER_UI_FAVICON_HREF": "SIDECAR", "REDOC_DIST": "SIDECAR", "SERVE_URLCONF": "weblate.api.urls", "TITLE": "Weblate's REST API", diff --git a/weblate/urls.py b/weblate/urls.py index eff4d21c6496..45c3b9dd736d 100644 --- a/weblate/urls.py +++ b/weblate/urls.py @@ -13,11 +13,7 @@ from django.views.decorators.cache import cache_control, cache_page from django.views.decorators.vary import vary_on_cookie from django.views.generic import RedirectView, TemplateView -from drf_spectacular.views import ( - SpectacularAPIView, - SpectacularRedocView, - SpectacularSwaggerView, -) +from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView import weblate.accounts.urls import weblate.accounts.views @@ -822,16 +818,7 @@ # OpenAPI schema path("api/schema/", SpectacularAPIView.as_view(), name="schema"), # API documentation - path( - "api/schema/swagger-ui/", - SpectacularSwaggerView.as_view(url_name="schema"), - name="swagger-ui", - ), - path( - "api/schema/redoc/", - SpectacularRedocView.as_view(url_name="schema"), - name="redoc", - ), + path("api/docs/", SpectacularRedocView.as_view(url_name="schema"), name="redoc"), # Static pages path("contact/", weblate.accounts.views.contact, name="contact"), path("hosting/", weblate.accounts.views.hosting, name="hosting"), From 8841711389434737eb4406ba37d03130a720d02c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 10:12:39 +0200 Subject: [PATCH 24/61] chore(settings): sync docker settings --- weblate/settings_docker.py | 2 -- weblate/settings_example.py | 5 ++++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/weblate/settings_docker.py b/weblate/settings_docker.py index d3c24d32c46d..61895591d53f 100644 --- a/weblate/settings_docker.py +++ b/weblate/settings_docker.py @@ -1209,8 +1209,6 @@ "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", } SPECTACULAR_SETTINGS = { - "SWAGGER_UI_DIST": "SIDECAR", - "SWAGGER_UI_FAVICON_HREF": "SIDECAR", "REDOC_DIST": "SIDECAR", "SERVE_URLCONF": "weblate.api.urls", "TITLE": "Weblate's REST API", diff --git a/weblate/settings_example.py b/weblate/settings_example.py index 5e170dcdbd5f..0da2eb4e049e 100644 --- a/weblate/settings_example.py +++ b/weblate/settings_example.py @@ -819,7 +819,10 @@ "weblate.api.throttling.UserRateThrottle", "weblate.api.throttling.AnonRateThrottle", ), - "DEFAULT_THROTTLE_RATES": {"anon": "100/day", "user": "5000/hour"}, + "DEFAULT_THROTTLE_RATES": { + "anon": "100/day", + "user": "5000/hour", + }, "DEFAULT_PAGINATION_CLASS": "weblate.api.pagination.StandardPagination", "PAGE_SIZE": 50, "VIEW_DESCRIPTION_FUNCTION": "weblate.api.views.get_view_description", From 305f60133a4dfc648c9f1348e44b4107f16990cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 10:17:12 +0200 Subject: [PATCH 25/61] docs: fix chapter level --- docs/contributing/frontend.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing/frontend.rst b/docs/contributing/frontend.rst index ca3259078c7a..622f41719c87 100644 --- a/docs/contributing/frontend.rst +++ b/docs/contributing/frontend.rst @@ -23,7 +23,7 @@ can be a bit tricky. This section provides a step-by-step guide on how to instal and manage 3rd party libraries used by the `client side` of Weblate using `Webpack`. Prerequisites -------------- ++++++++++++++ Before proceeding with an installation, make sure you have the following prerequisites: From bd19dcb6f6db1ff31b34e7be2c7e47dd7a06148e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 08:30:24 +0200 Subject: [PATCH 26/61] chore: use TextChoices for color choices This makes it behave more standard and allows to expose this nicely in spectator. --- weblate/settings_docker.py | 3 +++ weblate/settings_example.py | 3 +++ weblate/trans/models/component.py | 6 ++--- weblate/trans/models/label.py | 4 ++-- weblate/utils/colors.py | 37 ++++++++++++++++--------------- 5 files changed, 30 insertions(+), 23 deletions(-) diff --git a/weblate/settings_docker.py b/weblate/settings_docker.py index 61895591d53f..7a8c0b563ecc 100644 --- a/weblate/settings_docker.py +++ b/weblate/settings_docker.py @@ -1218,6 +1218,9 @@ The OpenAPI specification is available as feature preview, feedback welcome! """, "VERSION": None, + "ENUM_NAME_OVERRIDES": { + "ColorEnum": "weblate.utils.colors.ColorChoices.choices", + }, } # Fonts CDN URL diff --git a/weblate/settings_example.py b/weblate/settings_example.py index 0da2eb4e049e..4e7c6c2a701d 100644 --- a/weblate/settings_example.py +++ b/weblate/settings_example.py @@ -840,6 +840,9 @@ The OpenAPI specification is available as feature preview, feedback welcome! """, "VERSION": None, + "ENUM_NAME_OVERRIDES": { + "ColorEnum": "weblate.utils.colors.ColorChoices.choices", + }, } # Fonts CDN URL diff --git a/weblate/trans/models/component.py b/weblate/trans/models/component.py index ba964c9d1aa1..272d99bc11e8 100644 --- a/weblate/trans/models/component.py +++ b/weblate/trans/models/component.py @@ -73,7 +73,7 @@ ) from weblate.utils import messages from weblate.utils.celery import get_task_progress -from weblate.utils.colors import COLOR_CHOICES +from weblate.utils.colors import ColorChoices from weblate.utils.decorators import disable_for_loaddata from weblate.utils.errors import report_error from weblate.utils.fields import EmailField @@ -762,9 +762,9 @@ class Component(models.Model, PathMixin, CacheKeyMixin, ComponentCategoryMixin): glossary_color = models.CharField( verbose_name=gettext_lazy("Glossary color"), max_length=30, - choices=COLOR_CHOICES, + choices=ColorChoices.choices, blank=False, - default="silver", + default=ColorChoices.SILVER, ) remote_revision = models.CharField(max_length=200, default="", blank=True) local_revision = models.CharField(max_length=200, default="", blank=True) diff --git a/weblate/trans/models/label.py b/weblate/trans/models/label.py index 578dff0930ce..4797cd99ab3f 100644 --- a/weblate/trans/models/label.py +++ b/weblate/trans/models/label.py @@ -7,7 +7,7 @@ from django.utils.translation import gettext_lazy from weblate.checks.flags import Flags -from weblate.utils.colors import COLOR_CHOICES +from weblate.utils.colors import ColorChoices TRANSLATION_LABELS = {"Automatically translated"} @@ -20,7 +20,7 @@ class Label(models.Model): color = models.CharField( verbose_name=gettext_lazy("Color"), max_length=30, - choices=COLOR_CHOICES, + choices=ColorChoices.choices, blank=False, default=None, ) diff --git a/weblate/utils/colors.py b/weblate/utils/colors.py index d19a6233a72d..2b0a0f23ba72 100644 --- a/weblate/utils/colors.py +++ b/weblate/utils/colors.py @@ -2,39 +2,40 @@ # # SPDX-License-Identifier: GPL-3.0-or-later +from django.db.models import TextChoices from django.utils.translation import gettext_lazy -COLOR_CHOICES = ( + +class ColorChoices(TextChoices): # Translators: Name of a color - ("navy", gettext_lazy("Navy")), + NAVY = "navy", gettext_lazy("Navy") # Translators: Name of a color - ("blue", gettext_lazy("Blue")), + BLUE = "blue", gettext_lazy("Blue") # Translators: Name of a color - ("aqua", gettext_lazy("Aqua")), + AQUA = "aqua", gettext_lazy("Aqua") # Translators: Name of a color - ("teal", gettext_lazy("Teal")), + TEAL = "teal", gettext_lazy("Teal") # Translators: Name of a color - ("olive", gettext_lazy("Olive")), + OLIVE = "olive", gettext_lazy("Olive") # Translators: Name of a color - ("green", gettext_lazy("Green")), + GREEN = "green", gettext_lazy("Green") # Translators: Name of a color - ("lime", gettext_lazy("Lime")), + LIME = "lime", gettext_lazy("Lime") # Translators: Name of a color - ("yellow", gettext_lazy("Yellow")), + YELLOW = "yellow", gettext_lazy("Yellow") # Translators: Name of a color - ("orange", gettext_lazy("Orange")), + ORANGE = "orange", gettext_lazy("Orange") # Translators: Name of a color - ("red", gettext_lazy("Red")), + RED = "red", gettext_lazy("Red") # Translators: Name of a color - ("maroon", gettext_lazy("Maroon")), + MAROON = "maroon", gettext_lazy("Maroon") # Translators: Name of a color - ("fuchsia", gettext_lazy("Fuchsia")), + FUCHSIA = "fuchsia", gettext_lazy("Fuchsia") # Translators: Name of a color - ("purple", gettext_lazy("Purple")), + PURPLE = "purple", gettext_lazy("Purple") # Translators: Name of a color - ("black", gettext_lazy("Black")), + BLACK = "black", gettext_lazy("Black") # Translators: Name of a color - ("gray", gettext_lazy("Gray")), + GRAY = "gray", gettext_lazy("Gray") # Translators: Name of a color - ("silver", gettext_lazy("Silver")), -) + SILVER = "silver", gettext_lazy("Silver") From 9ba6239761411c68983c63938c07eb167be80161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 10:37:43 +0200 Subject: [PATCH 27/61] docs: remove stray bullet --- docs/changes.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/changes.rst b/docs/changes.rst index 67bc574eec52..bea7f3adee15 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -32,7 +32,6 @@ Not yet released. Please follow :ref:`generic-upgrade-instructions` in order to perform update. -* * There are several changes in :file:`settings_example.py`, most notable are the new settings for :ref:`api` in ``SPECTACULAR_SETTINGS`` and changes in ``REST_FRAMEWORK`` and ``INSTALLED_APPS``; please adjust your settings accordingly. **Contributors** From 6a1404db12ba4316fd0d72549c2cb77a5a0a0871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 09:13:11 +0200 Subject: [PATCH 28/61] feat(api): cleanup notifications API and document it The OpenAPI export now has correct documentation. --- weblate/api/views.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/weblate/api/views.py b/weblate/api/views.py index 69231dd862e1..f64d8620ab90 100644 --- a/weblate/api/views.py +++ b/weblate/api/views.py @@ -23,6 +23,7 @@ from django.utils.html import format_html from django.utils.translation import gettext from django_filters import rest_framework as filters +from drf_spectacular.utils import OpenApiParameter, extend_schema from rest_framework import parsers, viewsets from rest_framework.decorators import action from rest_framework.exceptions import ValidationError @@ -418,10 +419,16 @@ def groups(self, request: Request, **kwargs): return Response(serializer.data, status=HTTP_200_OK) + @extend_schema( + request=NotificationSerializer, + responses=NotificationSerializer(many=True), + ) @action( - detail=True, methods=["get", "post"], serializer_class=NotificationSerializer + detail=True, + methods=["get", "post"], + serializer_class=NotificationSerializer(many=True), ) - def notifications(self, request: Request, **kwargs): + def notifications(self, request: Request, username: str): obj = self.get_object() if request.method == "POST": self.perm_check(request) @@ -432,22 +439,29 @@ def notifications(self, request: Request, **kwargs): serializer.is_valid(raise_exception=True) serializer.save(user=obj) return Response(serializer.data, status=HTTP_201_CREATED) - queryset = obj.subscription_set.order_by("id") page = self.paginate_queryset(queryset) serializer = NotificationSerializer( page, many=True, context={"request": request} ) - return self.get_paginated_response(serializer.data) + @extend_schema( + parameters=[ + OpenApiParameter("subscription_id", int, OpenApiParameter.PATH), + ], + responses=NotificationSerializer, + request=NotificationSerializer, + ) @action( detail=True, methods=["get", "put", "patch", "delete"], url_path="notifications/(?P[0-9]+)", serializer_class=NotificationSerializer, ) - def notifications_details(self, request: Request, username, subscription_id): + def notifications_details( + self, request: Request, username: str, subscription_id: int + ): obj = self.get_object() try: From 18d5783c320edbeb77526015ff4ff2a832c717aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 11:08:36 +0200 Subject: [PATCH 29/61] fix(teams): clarify what no components selection means --- weblate/templates/auth/teams-components.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weblate/templates/auth/teams-components.html b/weblate/templates/auth/teams-components.html index f1430eb91488..326d9aee21fe 100644 --- a/weblate/templates/auth/teams-components.html +++ b/weblate/templates/auth/teams-components.html @@ -3,5 +3,5 @@ {% for component in group.components.all %} {{ component }} {% empty %} - {% trans "No components specified" %} + {% trans "All components" %} {% endfor %} From ecb443da2eef6897ef0adbcfd8539f0813732ce1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 11:10:44 +0200 Subject: [PATCH 30/61] chore(middleware): add compatiblity redirects to api docs --- weblate/middleware.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/weblate/middleware.py b/weblate/middleware.py index a95de8bbb497..70ba67e6dc0c 100644 --- a/weblate/middleware.py +++ b/weblate/middleware.py @@ -135,7 +135,11 @@ def should_redirect_with_slash(self, request: AuthenticatedHttpRequest): if ( path.endswith(("/", ".map")) or request.method != "GET" - or path.startswith(f"{settings.URL_PREFIX}/api") + or ( + path.startswith(f"{settings.URL_PREFIX}/api") + and not path.startswith(f"{settings.URL_PREFIX}/api/doc") + and not path.startswith(f"{settings.URL_PREFIX}/api/schema") + ) ): return False urlconf = getattr(request, "urlconf", None) From ae63c34f7799a338e5eaf62f5716404b9bfb92dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 11:54:17 +0200 Subject: [PATCH 31/61] chore: remove no longer needed CSP exceptions --- weblate/middleware.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/weblate/middleware.py b/weblate/middleware.py index 70ba67e6dc0c..58f276c26ee0 100644 --- a/weblate/middleware.py +++ b/weblate/middleware.py @@ -67,8 +67,6 @@ INLINE_PATHS = { "social:begin", "djangosaml2idp:saml_login_process", - "swagger-ui", - "redoc", } From 5610f46986668f7c5f8a0802d60c3706c9ecdfb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 12:39:12 +0200 Subject: [PATCH 32/61] chore: document blank direction choices on Language It is automatically updated on save(). --- .../0004_alter_language_direction.py | 30 +++++++++++++++++++ weblate/lang/models.py | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 weblate/lang/migrations/0004_alter_language_direction.py diff --git a/weblate/lang/migrations/0004_alter_language_direction.py b/weblate/lang/migrations/0004_alter_language_direction.py new file mode 100644 index 000000000000..a9aa73467e05 --- /dev/null +++ b/weblate/lang/migrations/0004_alter_language_direction.py @@ -0,0 +1,30 @@ +# Copyright © Michal Čihař +# +# SPDX-License-Identifier: GPL-3.0-or-later + +# Generated by Django 5.1.1 on 2024-10-01 10:38 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("lang", "0003_alter_plural_type"), + ] + + operations = [ + migrations.AlterField( + model_name="language", + name="direction", + field=models.CharField( + choices=[ + ("", "Automatically detect text direction"), + ("ltr", "Left to right"), + ("rtl", "Right to left"), + ], + default="", + max_length=3, + verbose_name="Text direction", + ), + ), + ] diff --git a/weblate/lang/models.py b/weblate/lang/models.py index 6162b12fdb0e..3ff5eb5fb93a 100644 --- a/weblate/lang/models.py +++ b/weblate/lang/models.py @@ -598,7 +598,7 @@ class Language(models.Model, CacheKeyMixin): max_length=3, default="", choices=( - ("", ""), + ("", gettext_lazy("Automatically detect text direction")), ("ltr", gettext_lazy("Left to right")), ("rtl", gettext_lazy("Right to left")), ), From f60acb22de1f6f0b32a938e279e327cd205648c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 13:03:26 +0200 Subject: [PATCH 33/61] chore(api): improve spectacular configuration - Factor out configuration as its is not something users will change - Use own template to avoid loading third-party fonts - Document rate-limiting headers - Add tests to validate OpenAPI endpoints --- weblate/api/docs.py | 113 +++++++++++++++++++ weblate/api/middleware.py | 11 +- weblate/api/spectacular.py | 64 +++++++++++ weblate/api/tests.py | 10 ++ weblate/middleware.py | 9 ++ weblate/settings_docker.py | 64 +++++------ weblate/settings_example.py | 17 +-- weblate/templates/drf_spectacular/redoc.html | 40 +++++++ weblate/urls.py | 6 +- 9 files changed, 277 insertions(+), 57 deletions(-) create mode 100644 weblate/api/docs.py create mode 100644 weblate/api/spectacular.py create mode 100644 weblate/templates/drf_spectacular/redoc.html diff --git a/weblate/api/docs.py b/weblate/api/docs.py new file mode 100644 index 000000000000..9a2a580c13fd --- /dev/null +++ b/weblate/api/docs.py @@ -0,0 +1,113 @@ +# Copyright © Michal Čihař +# +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import annotations + +from django.utils.translation import gettext +from drf_spectacular.plumbing import ( + ResolvedComponent, + build_basic_type, + build_parameter_type, +) +from drf_spectacular.settings import spectacular_settings +from drf_spectacular.utils import OpenApiParameter + +from .middleware import ( + RATELIMIT_LIMIT_HEADER, + RATELIMIT_REMAINING_HEADER, + RATELIMIT_RESET_HEADER, +) + + +def build_response_header_parameter( + name: str, + description: str, + schema_type: type = str, + required: bool = True, + **kwargs, +): + parameter = build_parameter_type( + name=name, + schema=build_basic_type(schema_type), + location=OpenApiParameter.HEADER, + description=description, + required=required, + **kwargs, + ) + + # following drf_spectacular.openapi.AutoSchema._get_response_headers_for_code, this is + # not present in header objects + del parameter["in"] + del parameter["name"] + + return parameter + + +def build_response_header_component( + name: str, + description: str, + schema_type: type = str, + required: bool = True, + **kwargs, +) -> ResolvedComponent: + parameter = build_response_header_parameter( + name=name, + description=description, + schema_type=schema_type, + required=required, + **kwargs, + ) + return ResolvedComponent( + name=name, + type=ResolvedComponent.HEADER, + schema=parameter, + object=name, + ) + + +RATELIMIT_LIMIT_COMPONENT = build_response_header_component( + name=RATELIMIT_LIMIT_HEADER, + schema_type=int, + description=gettext("Rate limiting limit of requests to perform"), +) +RATELIMIT_REMAINING_COMPONENT = build_response_header_component( + name=RATELIMIT_REMAINING_HEADER, + schema_type=int, + description=gettext("Remaining limit of requests"), +) +RATELIMIT_RESET_COMPONENT = build_response_header_component( + name=RATELIMIT_RESET_HEADER, + schema_type=int, + description=gettext("Number of seconds until ratelimit window resets"), +) + + +def add_middleware_headers(result, generator, request, public): + """Add headers to responses set by middleware.""" + generator.registry.register_on_missing(RATELIMIT_LIMIT_COMPONENT) + generator.registry.register_on_missing(RATELIMIT_REMAINING_COMPONENT) + generator.registry.register_on_missing(RATELIMIT_RESET_COMPONENT) + + for path in result["paths"].values(): + for operation in path.values(): + # the paths object may be extended with custom, non-standard extensions + if "responses" not in operation: # pragma: no cover + continue + + for response in operation["responses"].values(): + # spec: https://swagger.io/specification/#response-object + response.setdefault("headers", {}) + response["headers"].update( + { + RATELIMIT_LIMIT_HEADER: RATELIMIT_LIMIT_COMPONENT.ref, + RATELIMIT_REMAINING_HEADER: RATELIMIT_REMAINING_COMPONENT.ref, + RATELIMIT_RESET_HEADER: RATELIMIT_RESET_COMPONENT.ref, + } + ) + + result["components"] = generator.registry.build( + spectacular_settings.APPEND_COMPONENTS + ) + + return result diff --git a/weblate/api/middleware.py b/weblate/api/middleware.py index 6832c880bcf3..c2311860c6d7 100644 --- a/weblate/api/middleware.py +++ b/weblate/api/middleware.py @@ -10,6 +10,11 @@ from weblate.auth.models import AuthenticatedHttpRequest +RATELIMIT_LIMIT_HEADER = "X-RateLimit-Limit" +RATELIMIT_REMAINING_HEADER = "X-RateLimit-Remaining" +RATELIMIT_RESET_HEADER = "X-RateLimit-Reset" + + class ThrottlingMiddleware: def __init__(self, get_response=None) -> None: self.get_response = get_response @@ -18,8 +23,8 @@ def __call__(self, request: AuthenticatedHttpRequest): response = self.get_response(request) throttling = request.META.get("throttling_state", None) if throttling is not None: - response["X-RateLimit-Limit"] = throttling.num_requests - response["X-RateLimit-Remaining"] = throttling.num_requests - len( + response[RATELIMIT_LIMIT_HEADER] = throttling.num_requests + response[RATELIMIT_REMAINING_HEADER] = throttling.num_requests - len( throttling.history ) if throttling.history: @@ -28,5 +33,5 @@ def __call__(self, request: AuthenticatedHttpRequest): ) else: remaining_duration = throttling.duration - response["X-RateLimit-Reset"] = int(remaining_duration) + response[RATELIMIT_RESET_HEADER] = int(remaining_duration) return response diff --git a/weblate/api/spectacular.py b/weblate/api/spectacular.py new file mode 100644 index 000000000000..037cac3ce381 --- /dev/null +++ b/weblate/api/spectacular.py @@ -0,0 +1,64 @@ +# Copyright © Michal Čihař +# +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import annotations + +from django.utils.translation import gettext_lazy + + +def get_spectacular_settings(installed_apps: list[str]) -> None: + settings = { + # Use redoc from sidecar + # TODO: Should bundle it internally + "REDOC_DIST": "SIDECAR", + "REDOC_UI_SETTINGS": { + "theme": { + "typography": { + "fontFamily": '"Source Sans 3", sans-serif', + "headings": { + "fontFamily": '"Source Sans 3", sans-serif', + }, + "code": { + "fontFamily": '"Source Code Pro", monospace', + }, + }, + "logo": { + "maxWidth": "150px", + "maxHeight": "50vh", + "margin": "auto", + }, + }, + }, + # Document only API (not webauthn and other drf endpoints) + "SERVE_URLCONF": "weblate.api.urls", + "TITLE": gettext_lazy("Weblate's REST API"), + "LICENSE": { + "name": "GNU General Public License v3 or later", + "url": "https://docs.weblate.org/en/latest/contributing/license.html", + }, + "DESCRIPTION": """ +The API is accessible on the ``/api/`` URL and it is based on [Django REST framework](https://www.django-rest-framework.org/). + +The OpenAPI specification is available as feature preview, feedback welcome! + """, + "EXTENSIONS_INFO": { + "x-logo": { + "url": "/static/weblate.svg", + } + }, + # Do not use API versioning + "VERSION": None, + # Flatten enum definitions + "ENUM_NAME_OVERRIDES": { + "ColorEnum": "weblate.utils.colors.ColorChoices.choices", + }, + "POSTPROCESSING_HOOKS": [ + "drf_spectacular.hooks.postprocess_schema_enums", + "weblate.api.docs.add_middleware_headers", + ], + } + if "weblate.legal" in installed_apps: + settings["TOS"] = "/legal/terms/" + + return settings diff --git a/weblate/api/tests.py b/weblate/api/tests.py index 171fe9bc4ef5..cf4144227003 100644 --- a/weblate/api/tests.py +++ b/weblate/api/tests.py @@ -4533,3 +4533,13 @@ def test_create_label(self) -> None: }, code=403, ) + + +class OpenAPITest(APIBaseTest): + def test_view(self) -> None: + self.do_request( + "api-schema", + ) + + def test_redoc(self) -> None: + self.do_request("redoc") diff --git a/weblate/middleware.py b/weblate/middleware.py index 58f276c26ee0..0af2f062a677 100644 --- a/weblate/middleware.py +++ b/weblate/middleware.py @@ -306,6 +306,7 @@ def __init__( self.build_csp_static_url() self.build_csp_cdn() self.build_csp_auth() + self.build_csp_redoc() def apply_csp_settings(self) -> None: setting_names: dict[str, CSP_KIND] = { @@ -328,6 +329,14 @@ def add_csp_host(self, url: str, *directives: CSP_KIND) -> None | str: self.directives[directive].add(domain) return domain + def build_csp_redoc(self) -> None: + if ( + self.request.resolver_match + and self.request.resolver_match.view_name == "redoc" + ): + self.directives["script-src"].add("'unsafe-inline'") + self.directives["img-src"].add("data:") + def build_csp_inline(self) -> None: if ( self.request.resolver_match diff --git a/weblate/settings_docker.py b/weblate/settings_docker.py index 7a8c0b563ecc..766d0056b239 100644 --- a/weblate/settings_docker.py +++ b/weblate/settings_docker.py @@ -7,6 +7,7 @@ from django.core.exceptions import PermissionDenied from django.http import Http404 +from weblate.api.spectacular import get_spectacular_settings from weblate.utils.environment import ( get_env_bool, get_env_credentials, @@ -783,6 +784,30 @@ "drf_spectacular_sidecar", ] +# Legal integration +LEGAL_INTEGRATION = get_env_str("WEBLATE_LEGAL_INTEGRATION") +if LEGAL_INTEGRATION: + # Hosted Weblate legal documents + if LEGAL_INTEGRATION == "wllegal": + INSTALLED_APPS.append("wllegal") + + # Enable legal app + INSTALLED_APPS.append("weblate.legal") + + # TOS confirmation enforcement + if LEGAL_INTEGRATION in {"tos-confirm", "wllegal"}: + # Social auth pipeline to confirm TOS upon registration/subsequent sign in + SOCIAL_AUTH_PIPELINE.insert( + SOCIAL_AUTH_PIPELINE.index( + "weblate.accounts.pipeline.second_factor", + ) + + 1, + "weblate.legal.pipeline.tos_confirm", + ) + # Middleware to enforce TOS confirmation of signed in users + MIDDLEWARE.append("weblate.legal.middleware.RequireTOSMiddleware") + + modify_env_list(INSTALLED_APPS, "APPS") # Custom exception reporter to include some details @@ -1208,20 +1233,7 @@ "UNAUTHENTICATED_USER": "weblate.auth.models.get_anonymous", "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", } -SPECTACULAR_SETTINGS = { - "REDOC_DIST": "SIDECAR", - "SERVE_URLCONF": "weblate.api.urls", - "TITLE": "Weblate's REST API", - "DESCRIPTION": """ -The API is accessible on the ``/api/`` URL and it is based on [Django REST framework](https://www.django-rest-framework.org/). - -The OpenAPI specification is available as feature preview, feedback welcome! - """, - "VERSION": None, - "ENUM_NAME_OVERRIDES": { - "ColorEnum": "weblate.utils.colors.ColorChoices.choices", - }, -} +SPECTACULAR_SETTINGS = get_spectacular_settings(INSTALLED_APPS) # Fonts CDN URL FONTS_CDN_URL = None @@ -1430,30 +1442,6 @@ "WEBLATE_INTERLEDGER_PAYMENT_POINTERS", ["$ilp.uphold.com/ENU7fREdeZi9"] ) -# Legal integartion -LEGAL_INTEGRATION = get_env_str("WEBLATE_LEGAL_INTEGRATION") -if LEGAL_INTEGRATION: - # Hosted Weblate legal documents - if LEGAL_INTEGRATION == "wllegal": - INSTALLED_APPS.append("wllegal") - - # Enable legal app - INSTALLED_APPS.append("weblate.legal") - - # TOS confirmation enforcement - if LEGAL_INTEGRATION in {"tos-confirm", "wllegal"}: - # Social auth pipeline to confirm TOS upon registration/subsequent sign in - SOCIAL_AUTH_PIPELINE.insert( - SOCIAL_AUTH_PIPELINE.index( - "weblate.accounts.pipeline.second_factor", - ) - + 1, - "weblate.legal.pipeline.tos_confirm", - ) - # Middleware to enforce TOS confirmation of signed in users - MIDDLEWARE.append("weblate.legal.middleware.RequireTOSMiddleware") - - ADDITIONAL_CONFIG = "/app/data/settings-override.py" if os.path.exists(ADDITIONAL_CONFIG): with open(ADDITIONAL_CONFIG) as handle: diff --git a/weblate/settings_example.py b/weblate/settings_example.py index 4e7c6c2a701d..eeee75c3b2dc 100644 --- a/weblate/settings_example.py +++ b/weblate/settings_example.py @@ -9,6 +9,8 @@ import platform from logging.handlers import SysLogHandler +from weblate.api.spectacular import get_spectacular_settings + # Title of site to use SITE_TITLE = "Weblate" @@ -830,20 +832,7 @@ "UNAUTHENTICATED_USER": "weblate.auth.models.get_anonymous", "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", } -SPECTACULAR_SETTINGS = { - "REDOC_DIST": "SIDECAR", - "SERVE_URLCONF": "weblate.api.urls", - "TITLE": "Weblate's REST API", - "DESCRIPTION": """ -The API is accessible on the ``/api/`` URL and it is based on [Django REST framework](https://www.django-rest-framework.org/). - -The OpenAPI specification is available as feature preview, feedback welcome! - """, - "VERSION": None, - "ENUM_NAME_OVERRIDES": { - "ColorEnum": "weblate.utils.colors.ColorChoices.choices", - }, -} +SPECTACULAR_SETTINGS = get_spectacular_settings(INSTALLED_APPS) # Fonts CDN URL FONTS_CDN_URL = None diff --git a/weblate/templates/drf_spectacular/redoc.html b/weblate/templates/drf_spectacular/redoc.html new file mode 100644 index 000000000000..55c7c3570202 --- /dev/null +++ b/weblate/templates/drf_spectacular/redoc.html @@ -0,0 +1,40 @@ +{% load i18n %} +{% load static %} +{# Based on upstream template, removes Google fonts and add own font #} + + + + + {% block head %} + {% translate "Weblate's REST API" %} + + + {% if fonts_cdn_url %} + + + {% else %} + + + {% endif %} + + {% endblock head %} + + + {% block body %} + {% if settings %} +
+ + + {% else %} + + + {% endif %} + {% endblock body %} + + diff --git a/weblate/urls.py b/weblate/urls.py index 45c3b9dd736d..b410a93a5838 100644 --- a/weblate/urls.py +++ b/weblate/urls.py @@ -816,9 +816,11 @@ # Auth path("api/", include((weblate.api.urls, "weblate.api"), namespace="api")), # OpenAPI schema - path("api/schema/", SpectacularAPIView.as_view(), name="schema"), + path("api/schema/", SpectacularAPIView.as_view(), name="api-schema"), # API documentation - path("api/docs/", SpectacularRedocView.as_view(url_name="schema"), name="redoc"), + path( + "api/docs/", SpectacularRedocView.as_view(url_name="api-schema"), name="redoc" + ), # Static pages path("contact/", weblate.accounts.views.contact, name="contact"), path("hosting/", weblate.accounts.views.hosting, name="hosting"), From ef3e9a0ae0e83f4fd129afee34c391bcff299339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 15:20:30 +0200 Subject: [PATCH 34/61] feat(api): include servers in OpenAPI specification --- weblate/api/spectacular.py | 7 ++++++- weblate/settings_docker.py | 2 +- weblate/settings_example.py | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/weblate/api/spectacular.py b/weblate/api/spectacular.py index 037cac3ce381..19f415d07df0 100644 --- a/weblate/api/spectacular.py +++ b/weblate/api/spectacular.py @@ -7,7 +7,9 @@ from django.utils.translation import gettext_lazy -def get_spectacular_settings(installed_apps: list[str]) -> None: +def get_spectacular_settings( + installed_apps: list[str], site_url: str, site_title: str +) -> None: settings = { # Use redoc from sidecar # TODO: Should bundle it internally @@ -30,6 +32,9 @@ def get_spectacular_settings(installed_apps: list[str]) -> None: }, }, }, + "SERVERS": [ + {"url": site_url, "description": site_title}, + ], # Document only API (not webauthn and other drf endpoints) "SERVE_URLCONF": "weblate.api.urls", "TITLE": gettext_lazy("Weblate's REST API"), diff --git a/weblate/settings_docker.py b/weblate/settings_docker.py index 766d0056b239..738655111462 100644 --- a/weblate/settings_docker.py +++ b/weblate/settings_docker.py @@ -1233,7 +1233,7 @@ "UNAUTHENTICATED_USER": "weblate.auth.models.get_anonymous", "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", } -SPECTACULAR_SETTINGS = get_spectacular_settings(INSTALLED_APPS) +SPECTACULAR_SETTINGS = get_spectacular_settings(INSTALLED_APPS, SITE_URL, SITE_TITLE) # Fonts CDN URL FONTS_CDN_URL = None diff --git a/weblate/settings_example.py b/weblate/settings_example.py index eeee75c3b2dc..1ff296dd7ffd 100644 --- a/weblate/settings_example.py +++ b/weblate/settings_example.py @@ -832,7 +832,7 @@ "UNAUTHENTICATED_USER": "weblate.auth.models.get_anonymous", "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", } -SPECTACULAR_SETTINGS = get_spectacular_settings(INSTALLED_APPS) +SPECTACULAR_SETTINGS = get_spectacular_settings(INSTALLED_APPS, SITE_URL, SITE_TITLE) # Fonts CDN URL FONTS_CDN_URL = None From 7437afbc534685b313b46a2ce04e990de6ee7db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 15:22:06 +0200 Subject: [PATCH 35/61] fix(api): remove trailing slash from server in OpenAPI --- weblate/api/spectacular.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weblate/api/spectacular.py b/weblate/api/spectacular.py index 19f415d07df0..fc663effc4e8 100644 --- a/weblate/api/spectacular.py +++ b/weblate/api/spectacular.py @@ -33,7 +33,7 @@ def get_spectacular_settings( }, }, "SERVERS": [ - {"url": site_url, "description": site_title}, + {"url": site_url.rstrip("/"), "description": site_title}, ], # Document only API (not webauthn and other drf endpoints) "SERVE_URLCONF": "weblate.api.urls", From 71db2bfaccc22756b0acc0d2a1d77e06418909e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 15:17:40 +0200 Subject: [PATCH 36/61] feat: add OpenAPI lint to CI --- .github/matchers/spectacular.json | 16 +++++++ .github/matchers/spectacular.json.license | 5 ++ .github/workflows/api.yml | 58 +++++++++++++++++++++++ .gitignore | 1 + .reuse/dep5 | 2 +- redocly.yaml | 6 +++ 6 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 .github/matchers/spectacular.json create mode 100644 .github/matchers/spectacular.json.license create mode 100644 .github/workflows/api.yml create mode 100644 redocly.yaml diff --git a/.github/matchers/spectacular.json b/.github/matchers/spectacular.json new file mode 100644 index 000000000000..1c1f86a1bc23 --- /dev/null +++ b/.github/matchers/spectacular.json @@ -0,0 +1,16 @@ +{ + "problemMatcher": [ + { + "owner": "spectacular", + "pattern": [ + { + "file": 1, + "line": 2, + "severity": 3, + "message": 4, + "regexp": "^([^:]*):(\\d+): (Warning|Error) (.*)$" + } + ] + } + ] +} diff --git a/.github/matchers/spectacular.json.license b/.github/matchers/spectacular.json.license new file mode 100644 index 000000000000..015248065d24 --- /dev/null +++ b/.github/matchers/spectacular.json.license @@ -0,0 +1,5 @@ +Copyright © Michal Čihař + +SPDX-License-Identifier: CC0-1.0 + +This file is maintained in https://github.com/WeblateOrg/meta/ diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml new file mode 100644 index 000000000000..f377526d7f47 --- /dev/null +++ b/.github/workflows/api.yml @@ -0,0 +1,58 @@ +# Copyright © Michal Čihař +# +# SPDX-License-Identifier: GPL-3.0-or-later + +name: API + +on: + push: + branches-ignore: + - deepsource-fix-** + - renovate/** + - weblate + pull_request: + +permissions: + contents: read + +jobs: + api-lint: + runs-on: ubuntu-24.04 + name: API Lint + env: + CI_DATABASE: postgresql + CI_REDIS_HOST: 127.0.0.1 + CI_REDIS_PORT: '60001' + CI_DB_PASSWORD: weblate + CI_DB_HOST: 127.0.0.1 + CI_DB_PORT: '60000' + CI_SELENIUM: '1' + DJANGO_SETTINGS_MODULE: weblate.settings_test + PYTHONWARNINGS: default,ignore:unclosed:ResourceWarning + PYTHONUNBUFFERED: 1 + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: '' + cache-suffix: ${{ matrix.python-version }} + - name: Start services + run: ./ci/services-up $CI_DATABASE + - name: Install apt dependencies + run: sudo ./ci/apt-install $CI_DATABASE + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.12 + - name: Used versions + run: ./ci/print-versions + - name: Install Python dependencies + run: ./ci/pip-install latest + - name: Generate OpenAPI + run: | + echo "::add-matcher::.github/matchers/spectacular.json" + ./manage.py spectacular > weblate-openapi.yaml + echo "::remove-matcher owner=spectacular::" + - name: openapi-lint + run: npx @redocly/cli lint --format github-actions weblate-openapi.yaml diff --git a/.gitignore b/.gitignore index e70c329b0f2e..76c2473ceeee 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ weblate-*.tar.* /fil-result/ /weblate/static/js/vendor/oss-licenses.json *node_modules/ +/weblate-openapi.yaml diff --git a/.reuse/dep5 b/.reuse/dep5 index c45758514712..369cc145446b 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -19,7 +19,7 @@ Files: weblate/templates/* weblate/wladmin/templates/* weblate/legal/templates/* Copyright: Michal Čihař License: GPL-3.0-or-later -Files: .editorconfig .gitattributes .gitignore .gitmodules .imgbotconfig .pre-commit-config.yaml .pylintrc .readthedocs.yml .weblate MANIFEST.in codecov.yml pyproject.toml requirements*.txt dev-docker/environment scripts/yarn/.gitignore scripts/yarn/package.json scripts/yarn/yarn.lock setup.cfg docs/requirements.txt biome.json +Files: .editorconfig .gitattributes .gitignore .gitmodules .imgbotconfig .pre-commit-config.yaml .pylintrc .readthedocs.yml .weblate MANIFEST.in codecov.yml pyproject.toml requirements*.txt dev-docker/environment scripts/yarn/.gitignore scripts/yarn/package.json scripts/yarn/yarn.lock setup.cfg docs/requirements.txt biome.json redocly.yaml Copyright: Michal Čihař License: GPL-3.0-or-later diff --git a/redocly.yaml b/redocly.yaml new file mode 100644 index 000000000000..cba7652e7dfb --- /dev/null +++ b/redocly.yaml @@ -0,0 +1,6 @@ +extends: +- recommended +rules: + no-path-trailing-slash: off + # TODO: this should be and error + operation-summary: warn From f4f48ab42c09f7ce421153b5a09059ba66fe4ab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 15:47:22 +0200 Subject: [PATCH 37/61] fix(ci): add migration to api tests These needs database to be running. --- .github/workflows/api.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml index f377526d7f47..498d3faa9f93 100644 --- a/.github/workflows/api.yml +++ b/.github/workflows/api.yml @@ -49,6 +49,8 @@ jobs: run: ./ci/print-versions - name: Install Python dependencies run: ./ci/pip-install latest + - name: Migrate database + run: coverage run ./manage.py migrate --noinput --traceback - name: Generate OpenAPI run: | echo "::add-matcher::.github/matchers/spectacular.json" From 79fefd97eab436896a1dda717729cf9f346374c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 15:50:14 +0200 Subject: [PATCH 38/61] fix(ci): add migration to api tests These needs database to be running. --- .github/workflows/api.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml index 498d3faa9f93..7b951d4e4232 100644 --- a/.github/workflows/api.yml +++ b/.github/workflows/api.yml @@ -49,6 +49,8 @@ jobs: run: ./ci/print-versions - name: Install Python dependencies run: ./ci/pip-install latest + - name: Prepare database + run: ./ci/prepare-database - name: Migrate database run: coverage run ./manage.py migrate --noinput --traceback - name: Generate OpenAPI From 9695f912b0d24ae999d9442bb49719b4bb552696 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 13:50:53 +0000 Subject: [PATCH 39/61] chore(deps): update dependency qrcode to v8 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 976ea5ec6a57..43e34c0ffbbc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,7 +71,7 @@ dependencies = [ "pyparsing>=3.1.1,<3.2", "python-dateutil>=2.8.2", "python-redis-lock[django]>=4,<4.1", - "qrcode>=7.4.1,<8.0", + "qrcode>=7.4.1,<8.1", "rapidfuzz>=3.8.0,<3.11", "redis>=5.0.2,<5.2.0", "requests>=2.32.2,<2.33", From 48a79d20a7db2cf7feb6548f6be53b809201813e Mon Sep 17 00:00:00 2001 From: Mehdi Eloualy <92476321+meel-hd@users.noreply.github.com> Date: Tue, 1 Oct 2024 18:02:07 +0100 Subject: [PATCH 40/61] feat(changes): Add second paginator to the top of changes page (#12648) - Fixed bug of inactive input when a second paginator is added. --- weblate/static/loader-bootstrap.js | 37 ++++++++++++------- weblate/static/styles/main.css | 12 +++--- weblate/templates/paginator.html | 4 +- .../templates/snippets/position-field.html | 4 +- weblate/templates/trans/change_list.html | 2 +- 5 files changed, 34 insertions(+), 25 deletions(-) diff --git a/weblate/static/loader-bootstrap.js b/weblate/static/loader-bootstrap.js index 50e181724381..96d3d33ad8ae 100644 --- a/weblate/static/loader-bootstrap.js +++ b/weblate/static/loader-bootstrap.js @@ -1009,32 +1009,41 @@ $(function () { }); /* Click to edit position inline. Disable when clicked outside or pressed ESC */ - $("#position-input").on("click", function () { + const $positionInput = $(".position-input"); + const $positionInputEditable = $(".position-input-editable"); + const $positionInputEditableInput = $("#position-input-editable-input"); + $positionInput.on("click", function (event) { const $form = $(this).closest("form"); - $("#position-input").hide(); + $positionInput.hide(); $form.find("input[name=offset]").prop("disabled", false); - $("#position-input-editable").show(); - $("#position-input-editable-input").attr("type", "number").focus(); + $positionInputEditable.show(); + $positionInputEditableInput.attr("type", "number"); + $(event.target) + .closest(".pagination") + .find("#position-input-editable-input") + .focus(); document.addEventListener("click", clickedOutsideEditableInput); document.addEventListener("keyup", pressedEscape); }); const clickedOutsideEditableInput = (event) => { + // Check if clicked outside of the input and the editable input if ( - !$.contains($("#position-input-editable")[0], event.target) && - event.target !== $("#position-input")[0] + !$positionInputEditable.is(event.target) && + !$positionInputEditable.has(event.target).length && + !$positionInput.is(event.target) ) { - $("#position-input").show(); - $("#position-input-editable-input").attr("type", "hidden"); - $("#position-input-editable").hide(); - document.emoveEventListener("click", clickedOutsideEditableInput); + $positionInput.show(); + $positionInputEditableInput.attr("type", "hidden"); + $positionInputEditable.hide(); + document.removeEventListener("click", clickedOutsideEditableInput); document.removeEventListener("keyup", pressedEscape); } }; const pressedEscape = (event) => { - if (event.key === "Escape" && event.target !== $("#position-input")[0]) { - $("#position-input").show(); - $("#position-input-editable-input").attr("type", "hidden"); - $("#position-input-editable").hide(); + if (event.key === "Escape" && event.target !== $positionInput[0]) { + $positionInput.show(); + $positionInputEditableInput.attr("type", "hidden"); + $positionInputEditable.hide(); document.removeEventListener("click", clickedOutsideEditableInput); document.removeEventListener("keyup", pressedEscape); } diff --git a/weblate/static/styles/main.css b/weblate/static/styles/main.css index fec31f7041fb..1de5cbcb18ce 100644 --- a/weblate/static/styles/main.css +++ b/weblate/static/styles/main.css @@ -699,13 +699,13 @@ kbd { background-color: #f9f9f9; } -#position-input-editable { - padding: 2px 5px; +.position-input-editable { + padding: 0 !important; max-width: 9em; display: none; } -#position-input-editable input { +.position-input-editable input { height: 31px; padding-left: 2px; padding-right: 2px; @@ -713,14 +713,14 @@ kbd { } /* Chrome, Safari, Edge, Opera */ -#position-input-editable input::-webkit-outer-spin-button, -#position-input-editable input::-webkit-inner-spin-button { +.position-input-editable input::-webkit-outer-spin-button, +.position-input-editable input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } /* Firefox */ -#position-input-editable input[type="number"] { +.position-input-editable input[type="number"] { -moz-appearance: textfield; } diff --git a/weblate/templates/paginator.html b/weblate/templates/paginator.html index af9382bd292f..501b754c9075 100644 --- a/weblate/templates/paginator.html +++ b/weblate/templates/paginator.html @@ -6,10 +6,10 @@
  • {% if LANGUAGE_BIDI %}{% icon "page-last.svg" %}{% else %}{% icon "page-first.svg" %}{% endif %}
  • {% if LANGUAGE_BIDI %}{% icon "page-next.svg" %}{% else %}{% icon "page-previous.svg" %}{% endif %}
  • - + {% blocktrans with page_obj.number as position and page_obj.paginator.num_pages as total %}{{ position }} / {{ total }}{% endblocktrans %} - + {% if not paginator_form %}
    {% endif %} diff --git a/weblate/templates/snippets/position-field.html b/weblate/templates/snippets/position-field.html index e7d3d5572d06..29091a30db04 100644 --- a/weblate/templates/snippets/position-field.html +++ b/weblate/templates/snippets/position-field.html @@ -12,14 +12,14 @@ {% if LANGUAGE_BIDI %}{% icon "page-next.svg" %}{% else %}{% icon "page-previous.svg" %}{% endif %} {% endif %} {% endif %} -
    +
    {% if filter_pos %} {% blocktrans with filter_pos=filter_pos|intcomma filter_count=filter_count|intcomma %}{{ filter_pos }} / {{ filter_count }}{% endblocktrans %} {% else %} {% blocktrans count cnt=filter_count with count=filter_count|intcomma %}{{ count }} string{% plural %}{{ count }} strings{% endblocktrans %} {% endif %}
    -
    +
    {% blocktrans with filter_count=filter_count|intcomma %}/ {{ filter_count }}{% endblocktrans %} diff --git a/weblate/templates/trans/change_list.html b/weblate/templates/trans/change_list.html index 88147fed97fa..b7e7cb7d4a1e 100644 --- a/weblate/templates/trans/change_list.html +++ b/weblate/templates/trans/change_list.html @@ -35,8 +35,8 @@ {% block content %} {% if form.is_valid %} + {% include "paginator.html" %} {% include "last-changes-content.html" with last_changes=object_list %} - {% include "paginator.html" %} {% endif %} From 57f0f9b5280f59a7fe4531d164b7460b8cabd91b Mon Sep 17 00:00:00 2001 From: Tuomas Hietala Date: Tue, 1 Oct 2024 09:55:00 +0000 Subject: [PATCH 41/61] Translated using Weblate (Finnish) Currently translated at 90.3% (2912 of 3223 strings) Translation: Weblate/Application Translate-URL: https://hosted.weblate.org/projects/weblate/application/fi/ --- weblate/locale/fi/LC_MESSAGES/django.po | 26 +++++++------------------ 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/weblate/locale/fi/LC_MESSAGES/django.po b/weblate/locale/fi/LC_MESSAGES/django.po index 76590abfaa1e..182861a9f268 100644 --- a/weblate/locale/fi/LC_MESSAGES/django.po +++ b/weblate/locale/fi/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgstr "" "Project-Id-Version: Weblate 5.8\n" "Report-Msgid-Bugs-To: https://github.com/WeblateOrg/weblate/issues\n" "POT-Creation-Date: 2024-09-20 12:26+0000\n" -"PO-Revision-Date: 2024-09-26 16:32+0000\n" -"Last-Translator: Ricky Tigg \n" +"PO-Revision-Date: 2024-10-01 17:34+0000\n" +"Last-Translator: Tuomas Hietala \n" "Language-Team: Finnish \n" "Language: fi\n" @@ -5100,22 +5100,15 @@ msgid "Uses the specified formality if language is not specified as (in)formal" msgstr "" #: weblate/machinery/forms.py:308 -#, fuzzy -#| msgid "Translation comment" msgctxt "Automatic suggestion service configuration" msgid "Translation context" -msgstr "Käännöksen kommentti" +msgstr "Käännöksen asiayhteys" #: weblate/machinery/forms.py:312 -#, fuzzy -#| msgid "" -#| "Weblate could not parse the translation files while updating the " -#| "translations." msgid "" "Describe the context of the translation to improve the accuracy of the " "translation." -msgstr "" -"Weblate ei voi jäsentää käännöstiedostoja käännösten päivittämisen aikana." +msgstr "Kuvaile käännöksen asiayhteyttä käännöksen tarkkuuden parantamiseksi." #: weblate/machinery/forms.py:322 #, fuzzy @@ -5457,19 +5450,14 @@ msgstr "" "\"same-origin\" pyynnöille)." #: weblate/templates/403_csrf.html:18 -#, fuzzy -#| msgid "" -#| "This site requires a CSRF cookie when submitting forms. This cookie is " -#| "required for security reasons, to ensure that your browser is not being " -#| "hijacked by third-parties." msgid "" "Our site requires a session cookie to make the forms work. This cookie is " "required for security reasons, ensuring that your browser is not being " "hijacked by third parties." msgstr "" -"Tämä sivusto vaatii CSRF-evästeen vastaanottaessaan lomaketietoja. Eväste " -"vaaditaan turvallisuussyistä varmistamaan etteivät kolmannet osapuolet ole " -"ottaneet selaintasi haltuun." +"Tämä sivusto vaatii istuntoevästeen lomakkeita varten. Eväste vaaditaan " +"turvallisuussyistä varmistamaan etteivät kolmannet osapuolet ole kaapanneet " +"selaintasi." #: weblate/templates/403_csrf.html:19 msgid "" From 76b17d12c2bec8afd4a188b3b9b6c378ff541c7e Mon Sep 17 00:00:00 2001 From: Fjuro Date: Tue, 1 Oct 2024 11:53:42 +0000 Subject: [PATCH 42/61] Translated using Weblate (Czech) Currently translated at 100.0% (3223 of 3223 strings) Translation: Weblate/Application Translate-URL: https://hosted.weblate.org/projects/weblate/application/cs/ --- weblate/locale/cs/LC_MESSAGES/django.po | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/weblate/locale/cs/LC_MESSAGES/django.po b/weblate/locale/cs/LC_MESSAGES/django.po index 0aa3a6d065a9..c7f95ed71d0f 100644 --- a/weblate/locale/cs/LC_MESSAGES/django.po +++ b/weblate/locale/cs/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ msgstr "" "Project-Id-Version: Weblate 5.8\n" "Report-Msgid-Bugs-To: https://github.com/WeblateOrg/weblate/issues\n" "POT-Creation-Date: 2024-09-20 12:26+0000\n" -"PO-Revision-Date: 2024-09-24 08:31+0000\n" +"PO-Revision-Date: 2024-10-01 17:34+0000\n" "Last-Translator: Fjuro \n" "Language-Team: Czech \n" @@ -74,7 +74,7 @@ msgstr "Veřejný e-mail" #: weblate/accounts/forms.py:226 msgid "Do not publicly display e-mail address" -msgstr "Nezobrazujte veřejně e-mailovou adresu" +msgstr "Nezobrazovat e-mailovou adresu veřejně" #: weblate/accounts/forms.py:332 #, python-format @@ -747,8 +747,8 @@ msgid "" "Link to your Fediverse profile for federated services like Mastodon or " "diaspora*." msgstr "" -"Odkaz na váš Fediverse profil pro federované služby, jako je Mastodon nebo " -"diaspora *." +"Odkaz na váš profil Fediverse pro federované služby, jako je Mastodon nebo " +"diaspora*." #: weblate/accounts/models.py:617 msgid "Code site URL" @@ -756,8 +756,7 @@ msgstr "URL vývojářské stránky" #: weblate/accounts/models.py:620 msgid "Link to your code profile for services like Codeberg or GitLab." -msgstr "" -"Odkaz na váš vývojářský profil pro službami jako je Codeberg nebo GitLab." +msgstr "Odkaz na váš vývojářský profil pro služby jako Codeberg nebo GitLab." #: weblate/accounts/models.py:625 msgid "GitHub username" @@ -765,7 +764,7 @@ msgstr "Uživatelské jméno na GitHubu" #: weblate/accounts/models.py:630 msgid "X username" -msgstr "Uživatelské jméno na Xu" +msgstr "Uživatelské jméno na platformě X" #: weblate/accounts/models.py:635 msgid "LinkedIn profile name" @@ -5577,7 +5576,7 @@ msgstr "O Weblate" #: weblate/templates/about/index.html:18 weblate/templates/base.html:313 #: weblate/templates/footer.html:18 weblate/templates/manage/index.html:96 msgid "Donate to Weblate" -msgstr "Obdarujte Weblate" +msgstr "Přispět na Weblate" #: weblate/templates/about/index.html:25 msgid "Hosting translations" @@ -6188,8 +6187,8 @@ msgid "" "wherever your user profile appears." msgstr "" "Všechny položky na této stránce jsou volitelné a mohou být kdykoliv " -"odstraněny. Jejich vyplněním nám dáváte souhla sdílet tyto údaje kdykoliv se " -"zobrazí váš veřejný profil." +"odstraněny. Jejich vyplněním nám dáváte souhlas sdílet tyto údaje kdekoliv, " +"kde je vidět váš veřejný profil." #: weblate/templates/accounts/profile.html:312 msgid "Your avatar" @@ -6198,7 +6197,7 @@ msgstr "Váš avatar" #: weblate/templates/accounts/profile.html:315 #, python-format msgid "Avatars are provided using %(avatar_url_prefix)s." -msgstr "Avatary jsou poskytovány prostřednictvím %(avatar_url_prefix)s." +msgstr "Avatary jsou poskytovány prostřednictvím služby %(avatar_url_prefix)s." #: weblate/templates/accounts/profile.html:365 #: weblate/templates/trans/project-access.html:95 From b5d508ced7bde7ec018b8b8b99907f02f44e506d Mon Sep 17 00:00:00 2001 From: Reno Tx Date: Mon, 30 Sep 2024 22:43:13 +0000 Subject: [PATCH 43/61] Translated using Weblate (Serbian) Currently translated at 68.7% (2215 of 3223 strings) Translation: Weblate/Application Translate-URL: https://hosted.weblate.org/projects/weblate/application/sr/ --- weblate/locale/sr/LC_MESSAGES/django.po | 275 ++++++++++++++---------- 1 file changed, 163 insertions(+), 112 deletions(-) diff --git a/weblate/locale/sr/LC_MESSAGES/django.po b/weblate/locale/sr/LC_MESSAGES/django.po index 21a142b1f59b..d6d13bbe2383 100644 --- a/weblate/locale/sr/LC_MESSAGES/django.po +++ b/weblate/locale/sr/LC_MESSAGES/django.po @@ -12,7 +12,7 @@ msgstr "" "Project-Id-Version: Weblate 5.8\n" "Report-Msgid-Bugs-To: https://github.com/WeblateOrg/weblate/issues\n" "POT-Creation-Date: 2024-09-20 12:26+0000\n" -"PO-Revision-Date: 2024-09-30 13:16+0000\n" +"PO-Revision-Date: 2024-10-01 17:34+0000\n" "Last-Translator: Reno Tx \n" "Language-Team: Serbian \n" @@ -27,7 +27,7 @@ msgstr "" #: weblate/accounts/avatar.py:84 msgctxt "No known user" msgid "None" -msgstr "Непознат" +msgstr "Нема" #: weblate/accounts/avatar.py:109 weblate/templates/accounts/profile.html:314 #: weblate/templates/accounts/user.html:23 weblate/templates/base.html:270 @@ -43,7 +43,7 @@ msgstr "Ова е-адреса се већ користи. Дајте другу #: weblate/accounts/forms.py:113 msgid "This username is already taken. Please choose another." -msgstr "Корисничко име је заузето. Изаберите друго." +msgstr "Ово корисничко име је већ заузето. Молимо изаберите друго." #: weblate/accounts/forms.py:124 weblate/accounts/forms.py:884 #: weblate/auth/models.py:407 weblate/auth/models.py:1108 @@ -146,7 +146,7 @@ msgstr "Потврда нове лозинке" #: weblate/accounts/forms.py:508 msgid "Your password has been changed." -msgstr "Ваша лозинка је измењена." +msgstr "Ваша лозинка је промењена." #: weblate/accounts/forms.py:535 #, python-format @@ -170,7 +170,7 @@ msgstr "Оставите празно ако још нисте поставил #: weblate/accounts/forms.py:596 msgid "You have entered an invalid password." -msgstr "Унели сте погрешну лозинку." +msgstr "Унели сте неважећу лозинку." #: weblate/accounts/forms.py:610 msgid "Username or e-mail" @@ -309,11 +309,11 @@ msgstr "Датум придруживања" #: weblate/accounts/forms.py:886 msgid "Translations made" -msgstr "Превода дато" +msgstr "Направљени преводи" #: weblate/accounts/forms.py:887 msgid "Suggestions made" -msgstr "Предлози су додати" +msgstr "Дате сугестије" #: weblate/accounts/forms.py:888 msgid "Comments made" @@ -564,11 +564,11 @@ msgstr "Активност налога" #: weblate/accounts/models.py:437 msgid "Interface Language" -msgstr "Језик окружења" +msgstr "Језик интерфејса" #: weblate/accounts/models.py:443 msgid "Translated languages" -msgstr "Језици превода" +msgstr "Преведени језици" #: weblate/accounts/models.py:446 msgid "" @@ -738,16 +738,20 @@ msgid "" "Liberapay is a platform to donate money to teams, organizations and " "individuals." msgstr "" +"Liberapay је платформа за донирање новца тимовима, организацијама и " +"појединцима." #: weblate/accounts/models.py:608 msgid "Fediverse URL" -msgstr "" +msgstr "Fediverse URL" #: weblate/accounts/models.py:611 msgid "" "Link to your Fediverse profile for federated services like Mastodon or " "diaspora*." msgstr "" +"Веза до вашег Fediverse профила за федеративне услуге као што су Mastodon " +"или diaspora*." #: weblate/accounts/models.py:617 msgid "Code site URL" @@ -771,7 +775,7 @@ msgstr "Име профила на Линкдину" #: weblate/accounts/models.py:637 msgid "Your LinkedIn profile name from linkedin.com/in/profilename" -msgstr "" +msgstr "Ваш LinkedIn профил име са linkedin.com/in/profilename" #: weblate/accounts/models.py:644 #: weblate/templates/snippets/embed-units.html:64 @@ -1015,7 +1019,7 @@ msgstr "Аутентификациона апликација (%s)" #: weblate/accounts/views.py:1112 weblate/templates/accounts/reset.html:7 #: weblate/templates/accounts/reset.html:36 msgid "Password reset" -msgstr "Ресетуј лозинку" +msgstr "Ресетовање лозинке" #: weblate/accounts/views.py:208 msgid "Remove account" @@ -1031,7 +1035,7 @@ msgstr "Не могу да пошаљем поруку администрато #: weblate/accounts/views.py:273 msgid "Your request has been sent, you will shortly hear from us." -msgstr "" +msgstr "Ваш захтев је послат, ускоро ћете чути од нас." #: weblate/accounts/views.py:399 msgid "Your profile has been updated." @@ -1063,12 +1067,16 @@ msgid "" "Seems you've already requested a trial period recently. Please contact us " "with your inquiry so we can find the best solution for you." msgstr "" +"Чини се да сте већ недавно затражили пробни период. Молимо контактирајте нас " +"са вашим упитом како бисмо пронашли најбоље решење за вас." #: weblate/accounts/views.py:622 msgid "" "Your trial period is now up and running; create your translation project and " "start Weblating!" msgstr "" +"Ваш пробни период је сада активан; креирајте свој пројекат превода и почните " +"са Веблејтингом!" #: weblate/accounts/views.py:628 weblate/templates/accounts/hosting.html:62 #: weblate/templates/accounts/trial.html:7 @@ -1106,7 +1114,7 @@ msgstr "" #: weblate/templates/accounts/profile.html:144 #: weblate/wladmin/templates/admin/base.html:12 msgid "Change password" -msgstr "Измени лозинку" +msgstr "Промени лозинку" #: weblate/accounts/views.py:1032 msgid "Password reset has been already completed." @@ -1133,11 +1141,12 @@ msgstr "Не могу да довршим регистрацију." #: weblate/accounts/views.py:1348 msgid "Please check if you have already registered an account." -msgstr "" +msgstr "Молимо проверите да ли сте већ регистровали налог." #: weblate/accounts/views.py:1351 msgid "You can also request a new password, if you have lost your credentials." msgstr "" +"Такође можете затражити нову лозинку, ако сте изгубили своје акредитиве." #: weblate/accounts/views.py:1362 msgid "" @@ -1257,6 +1266,8 @@ msgid "" "Publishes translations into content delivery network for use in JavaScript " "or HTML localization." msgstr "" +"Објављује преводе у мрежу за испоруку садржаја за употребу у JavaScript или " +"HTML локализацији." #: weblate/addons/cleanup.py:35 msgid "Cleanup translation files" @@ -2296,7 +2307,7 @@ msgstr "Управља корисницима" #. Translators: Permission name #: weblate/auth/data.py:131 weblate/templates/manage/users.html:18 msgid "Manage users" -msgstr "Управља корисницима" +msgstr "Управљање корисницима" #. Translators: Permission name #: weblate/auth/data.py:133 @@ -2523,7 +2534,7 @@ msgstr "%s (дозвола за цео сајт)" #: weblate/templates/trans/project-access.html:191 #: weblate/templates/trans/project-access.html:282 weblate/trans/forms.py:1520 msgid "Name" -msgstr "Назив" +msgstr "Име" #: weblate/auth/models.py:106 msgid "Choose permissions granted to this role." @@ -2798,7 +2809,7 @@ msgstr "Гласање за предлоге је онемогућено." #: weblate/auth/permissions.py:227 msgid "" "This translation only accepts suggestions, and these are approved by voting." -msgstr "" +msgstr "Овај превод прихвата само предлоге, и они су одобрени гласањем." #: weblate/auth/permissions.py:233 #: weblate/templates/snippets/project/state.html:6 @@ -2890,7 +2901,7 @@ msgstr "Додатна порука" #: weblate/billing/forms.py:18 msgid "" "Please describe the project and your relation to it, preferably in English." -msgstr "" +msgstr "Молимо опишите пројекат и вашу везу са њим, пожељно на енглеском." #: weblate/billing/models.py:155 weblate/templates/billing/detail.html:24 msgid "Billing plan" @@ -3009,6 +3020,8 @@ msgid "" "You will stop receiving this notification once you change to regular " "subscription or the project is removed." msgstr "" +"Престаћете да примате ово обавештење када промените на редовну претплату или " +"пројекат буде уклоњен." #: weblate/checks/angularjs.py:32 msgid "AngularJS interpolation string" @@ -3274,7 +3287,7 @@ msgstr "ДОС завршетак линије" #: weblate/templates/data.html:30 weblate/templates/data.html:64 #: weblate/templates/data.html:105 weblate/templates/data.html:139 msgid "URL" -msgstr "УРЛ" +msgstr "URL" #: weblate/checks/flags.py:47 msgid "Automatically detect Java MessageFormat" @@ -3864,11 +3877,11 @@ msgstr "Држач за проценат се не поклапа са изво #: weblate/checks/format.py:743 msgid "Vue I18n formatting" -msgstr "" +msgstr "Vue I18n форматирање" #: weblate/checks/format.py:744 msgid "The Vue I18n formatting does not match source" -msgstr "" +msgstr "Vue I18n форматирање не одговара извору" #: weblate/checks/format.py:752 msgid "Multiple unnamed variables" @@ -4144,7 +4157,7 @@ msgstr "Ниска није преведена дуго времена" #: weblate/templates/language.html:32 weblate/templates/project.html:75 #: weblate/templates/translation.html:76 msgid "Failing checks" -msgstr "Грешке при провери" +msgstr "Неуспеле провере" #: weblate/checks/views.py:226 msgid "Check" @@ -4364,7 +4377,7 @@ msgstr "" #: weblate/formats/ttkit.py:1322 msgid "iOS strings (UTF-16)" -msgstr "" +msgstr "iOS низови (UTF-16)" #: weblate/formats/ttkit.py:1331 msgid "iOS strings (UTF-8)" @@ -4524,7 +4537,7 @@ msgstr "ИНИ фајл" #: weblate/formats/ttkit.py:1875 msgid "Inno Setup INI file" -msgstr "" +msgstr "Inno Setup INI фајл" #: weblate/formats/ttkit.py:2044 msgid "TermBase eXchange file" @@ -4558,7 +4571,7 @@ msgstr "Датотеке метаподатака продавнице апли #: weblate/glossary/apps.py:12 msgid "Glossaries" -msgstr "Појмовници" +msgstr "Глосари" #: weblate/glossary/forms.py:29 msgid "Invalid integer list!" @@ -4943,7 +4956,7 @@ msgstr "Молимо прочитајте следећи документ Усл #: weblate/templates/contributor-agreement.html:30 #: weblate/templates/manage/ssh.html:79 msgid "Submit" -msgstr "Пошаљи" +msgstr "Предај" #: weblate/legal/templates/legal/contracts.html:7 #: weblate/legal/templates/legal/privacy.html:7 weblate/legal/views.py:21 @@ -4952,7 +4965,7 @@ msgstr "Приватност" #: weblate/legal/templates/legal/contracts.html:15 weblate/legal/views.py:22 msgid "Subcontractors" -msgstr "" +msgstr "Подизвођачи" #: weblate/legal/templates/legal/cookies.html:6 weblate/legal/views.py:20 msgid "Cookies" @@ -5691,7 +5704,7 @@ msgstr "Страница није нађена" #: weblate/templates/404.html:13 msgid "The page you are looking for was not found." -msgstr "Страница коју сте тражили није пронађена." +msgstr "Страница коју тражите није пронађена." #: weblate/templates/404.html:14 msgid "" @@ -5704,7 +5717,7 @@ msgstr "Интерна грешка сервера" #: weblate/templates/500.html:11 msgid "Server Error" -msgstr "Грешка сервера" +msgstr "Грешка на серверу" #: weblate/templates/500.html:16 #, python-format @@ -5818,7 +5831,7 @@ msgstr "Бесплатна проба комерцијалног хостинг #: weblate/templates/about/index.html:41 msgid "Contact us for commercial hosting" -msgstr "Контактирајте нас ради комерцијалног хостинга" +msgstr "Контактирајте нас за комерцијални хостинг" #: weblate/templates/about/index.html:49 msgid "Weblate is built on libre software" @@ -5989,7 +6002,7 @@ msgstr "Уклањање налога је при крају" #: weblate/templates/accounts/email-sent.html:20 msgid "Registration almost complete" -msgstr "Регистрација је скоро готова" +msgstr "Регистрација је скоро завршена" #: weblate/templates/accounts/email-sent.html:26 msgid "Follow the password recovery instructions sent to your e-mail inbox." @@ -6043,7 +6056,7 @@ msgstr "Ако имате проблем са регистрацијом, кон #: weblate/templates/accounts/register.html:25 #: weblate/templates/accounts/reset.html:23 msgid "Please fix errors in the registration form." -msgstr "Исправите грешке у обрасцу за регистрацију." +msgstr "Молимо исправите грешке у формулару за регистрацију." #: weblate/templates/accounts/email.html:17 #: weblate/templates/accounts/profile.html:126 @@ -6056,7 +6069,7 @@ msgstr "Е-пошта корисника" #: weblate/templates/accounts/register.html:52 weblate/templates/base.html:231 #: weblate/templates/engage.html:62 msgid "Register" -msgstr "Регистрација" +msgstr "Региструј се" #: weblate/templates/accounts/hosting.html:16 msgid "Trial subscriptions" @@ -6076,15 +6089,15 @@ msgstr "Ваша пробна понуда" #: weblate/templates/accounts/hosting.html:37 msgid "You will have 14 days to set up your project." -msgstr "" +msgstr "Имаћете 14 дана да подесите свој пројекат." #: weblate/templates/accounts/hosting.html:38 msgid "Please request project approval once you finish the setup." -msgstr "" +msgstr "Молимо затражите одобрење пројекта када завршите подешавање." #: weblate/templates/accounts/hosting.html:39 msgid "Unapproved projects will be removed." -msgstr "" +msgstr "Неодобрени пројекти ће бити уклоњени." #: weblate/templates/accounts/hosting.html:40 msgid "" @@ -6103,7 +6116,8 @@ msgstr "Тражите комерцијални хостинг?" #: weblate/templates/accounts/login.html:16 msgid "This username/password combination was not found. Please try again." msgstr "" -"Ова комбинација корисничког имена и лозинке није нађена. Покушајте поново." +"Ова комбинација корисничког имена и лозинке није пронађена. Молимо покушајте " +"поново." #: weblate/templates/accounts/login.html:44 msgid "Sign in with:" @@ -6139,26 +6153,27 @@ msgid "" "Please enter your new password twice so we can verify you typed it in " "correctly." msgstr "" -"Унесите вашу нову лозинку два пута да би проверили да је исправно унета." +"Молимо унесите нову лозинку два пута како бисмо могли да проверимо да ли сте " +"је исправно унели." #: weblate/templates/accounts/password.html:23 msgid "Change my password" -msgstr "Измени моју лозинку" +msgstr "Промени моју лозинку" #: weblate/templates/accounts/profile.html:16 weblate/trans/views/basic.py:780 #: weblate/trans/views/files.py:205 msgid "Please fix errors in the form." -msgstr "Исправите грешке у обрасцу." +msgstr "Молимо исправите грешке у формулару." #: weblate/templates/accounts/profile.html:26 #: weblate/templates/accounts/profile.html:55 msgid "Preferences" -msgstr "Подешавања" +msgstr "Преференце" #: weblate/templates/accounts/profile.html:27 #: weblate/templates/accounts/user.html:157 msgid "Notifications" -msgstr "Обавештења" +msgstr "Нотификације" #: weblate/templates/accounts/profile.html:28 #: weblate/templates/accounts/profile.html:117 @@ -6261,7 +6276,7 @@ msgstr "Постави лозинку" #: weblate/templates/accounts/profile.html:156 msgid "Disconnect" -msgstr "Искључи се" +msgstr "Искључи" #: weblate/templates/accounts/profile.html:173 #, fuzzy @@ -6406,6 +6421,9 @@ msgid "" "and by filling them out, you're giving us consent to share this data " "wherever your user profile appears." msgstr "" +"Сва поља на овој страници су опционална и могу се обрисати у било ком " +"тренутку, а попуњавањем истих, дајете нам сагласност да делимо ове податке " +"где год се појављује ваш кориснички профил." #: weblate/templates/accounts/profile.html:312 msgid "Your avatar" @@ -6609,7 +6627,7 @@ msgstr "Уклањање налога" msgid "" "By pressing following button, your will no longer be able to use this " "account." -msgstr "Притиском на следеће дугме, нећете више моћи да користите овај налог." +msgstr "Притиском на следеће дугме, више нећете моћи да користите овај налог." #: weblate/templates/accounts/removal.html:21 msgid "" @@ -6685,10 +6703,12 @@ msgstr "" #: weblate/templates/accounts/trial.html:25 msgid "No credit card is needed, but you can set the payment method anytime." msgstr "" +"Кредитна картица није потребна, али можете подесити начин плаћања у било ком " +"тренутку." #: weblate/templates/accounts/trial.html:26 msgid "All configuration will stay once you start paying." -msgstr "" +msgstr "Сва конфигурација ће остати када почнете да плаћате." #: weblate/templates/accounts/trial.html:30 msgid "Start the trial!" @@ -6840,7 +6860,7 @@ msgstr "Идентитет" #: weblate/templates/accounts/user.html:212 msgid "User ID" -msgstr "ИД корисника" +msgstr "Кориснички ИД" #: weblate/templates/accounts/user.html:236 #, fuzzy @@ -6937,7 +6957,7 @@ msgstr "Уклањање налога брише све ваше личне по #: weblate/templates/trans/project-access.html:221 #: weblate/templates/trans/project-access.html:241 msgid "Delete" -msgstr "Избриши" +msgstr "Обриши" #: weblate/templates/accounts/user.html:383 #: weblate/templates/auth/team.html:159 weblate/templates/auth/team.html:180 @@ -7052,7 +7072,7 @@ msgstr "Управљање" #: weblate/templates/language.html:26 weblate/templates/project.html:41 #: weblate/templates/translate.html:264 weblate/templates/translation.html:44 msgid "History" -msgstr "Историјат" +msgstr "Историја" #: weblate/templates/addons/addon_list.html:31 msgid "No add-ons currently installed" @@ -7163,13 +7183,13 @@ msgstr "" #: weblate/templates/snippets/position-field.html:9 #: weblate/templates/snippets/position-field.html:12 msgid "Previous" -msgstr "Претходна" +msgstr "Претходни" #: weblate/templates/addons/addon_logs.html:74 #: weblate/templates/snippets/position-field.html:30 #: weblate/templates/snippets/position-field.html:33 msgid "Next" -msgstr "Следећа" +msgstr "Следећи" #: weblate/templates/addons/cdnjs.html:5 msgid "Integration instructions" @@ -7181,7 +7201,7 @@ msgstr "Укључите следећи исечак да учитате лок #: weblate/templates/addons/cdnjs.html:13 msgid "The localizable elements have to match configured CSS selector." -msgstr "" +msgstr "Локализовани елементи морају одговарати конфигурисаном CSS селектору." #: weblate/templates/addons/discovery_preview.html:8 msgid "Matched files" @@ -7259,7 +7279,7 @@ msgstr "Слаг URL-а компоненте" #: weblate/templates/addons/generate_help.html:26 #: weblate/trans/models/project.py:136 msgid "Project name" -msgstr "Назив пројекта" +msgstr "Име пројекта" #: weblate/templates/addons/generate_help.html:28 msgid "Project URL slug" @@ -7372,7 +7392,7 @@ msgstr "Додај корисника" #: weblate/templates/trans/project-access-row.html:52 #, python-format msgid "This will remove %(user)s access to this project." -msgstr "" +msgstr "Ово ће уклонити %(user)s приступ овом пројекту." #: weblate/templates/auth/team.html:188 weblate/templates/base.html:241 #: weblate/templates/category.html:185 weblate/templates/category.html:238 @@ -7429,7 +7449,7 @@ msgstr "Контрола приступа" #: weblate/templates/base.html:119 msgid "Toggle navigation" -msgstr "Навигација" +msgstr "Пребаци навигацију" #: weblate/templates/base.html:131 weblate/templates/category-project.html:16 #: weblate/templates/category.html:31 weblate/templates/component.html:38 @@ -7446,7 +7466,7 @@ msgstr "Навигација" #: weblate/templates/trans/change_list.html:50 #: weblate/templates/translate.html:383 weblate/templates/translation.html:38 msgid "Search" -msgstr "Тражи" +msgstr "Претрага" #: weblate/templates/base.html:146 msgid "Manage watched projects" @@ -7495,7 +7515,7 @@ msgstr "Администрација" #: weblate/templates/base.html:218 weblate/templates/base.html:222 #: weblate/templates/manage/performance.html:19 msgid "Configuration errors" -msgstr "Грешке поставе" +msgstr "Грешке у конфигурацији" #: weblate/templates/base.html:245 weblate/templates/billing/status.html:113 #: weblate/templates/trans/project_form.html:23 @@ -7520,7 +7540,7 @@ msgstr "Додај нову компоненту превода" #: weblate/templates/snippets/list-objects.html:288 #: weblate/templates/snippets/list-objects.html:290 msgid "Start new translation" -msgstr "Започните нови превод" +msgstr "Започни нови превод" #: weblate/templates/base.html:256 msgid "Try Weblate for free" @@ -7670,10 +7690,12 @@ msgid "" "This subscription is not eligible to the libre plan, please fix the errors " "above or purchase a subscription." msgstr "" +"Ова претплата није подобна за либре план, молимо исправите грешке изнад или " +"купите претплату." #: weblate/templates/billing/list.html:10 msgid "Plan" -msgstr "" +msgstr "План" #: weblate/templates/billing/list.html:11 weblate/wladmin/models.py:214 msgid "Hosted words" @@ -7733,7 +7755,7 @@ msgstr "Измени план" #: weblate/templates/billing/status.html:31 #: weblate/templates/billing/status.html:42 msgid "Extend trial" -msgstr "" +msgstr "Продужи пробни период" #: weblate/templates/billing/status.html:48 msgid "Monthly price" @@ -8221,7 +8243,7 @@ msgstr "" #: weblate/templates/component.html:273 weblate/templates/project.html:291 #: weblate/templates/translate.html:366 weblate/templates/translation.html:474 msgid "Loading…" -msgstr "Учитавам…" +msgstr "Учитавање…" #: weblate/templates/component.html:294 msgid "" @@ -8318,7 +8340,7 @@ msgstr "Разни подаци из Веблејта доступни су у #: weblate/templates/data.html:18 msgid "RSS feeds" -msgstr "РСС доводи" +msgstr "RSS фидови" #: weblate/templates/data.html:23 msgid "" @@ -8328,7 +8350,7 @@ msgstr "Пратите напредак превођења и све битне #: weblate/templates/data.html:31 weblate/templates/data.html:65 #: weblate/templates/data.html:106 msgid "Link" -msgstr "Линк" +msgstr "Веза" #: weblate/templates/data.html:37 weblate/templates/data.html:43 msgid "RSS" @@ -8339,12 +8361,16 @@ msgid "" "Per language RSS feeds are also available. Construct them by appending a " "language code to the URLs above." msgstr "" +"RSS фидови по језику су такође доступни. Конструишите их додавањем језичког " +"кода на горње URL-ове." #: weblate/templates/data.html:57 msgid "" "Translation statistics for every component in JSON format lets you use the " "data in other websites or tools." msgstr "" +"Статистика превода за сваки компонент у JSON формату вам омогућава да " +"користите податке на другим вебсајтовима или алатима." #: weblate/templates/data.html:80 msgid "" @@ -8382,7 +8408,7 @@ msgstr "Хостинг сајт" #: weblate/templates/data.html:140 msgid "Note" -msgstr "Белешка" +msgstr "Напомена" #: weblate/templates/data.html:147 weblate/templates/data.html:152 #: weblate/templates/data.html:157 weblate/templates/data.html:162 @@ -8744,7 +8770,7 @@ msgstr "Статус складишта" #: weblate/templates/js/git-status.html:26 msgid "Commit" -msgstr "Отпреми" +msgstr "Урезивање" #: weblate/templates/js/git-status.html:29 #, fuzzy, python-format @@ -8790,7 +8816,7 @@ msgstr[2] "%(count)s коментара" #: weblate/templates/js/git-status.html:54 msgid "Update" -msgstr "" +msgstr "Ажурирање" #: weblate/templates/js/git-status.html:61 msgid "Update with merge" @@ -8967,7 +8993,7 @@ msgstr "Детаљи складишта" #: weblate/templates/js/git-status.html:178 msgid "Repository changes" -msgstr "Измене складишта" +msgstr "Промене у складишту" #: weblate/templates/lang/create.html:7 #: weblate/templates/snippets/list-objects.html:31 @@ -9040,7 +9066,7 @@ msgstr "Решени коментар" #: weblate/templates/list-comments.html:15 msgid "Source string comment" -msgstr "Коментар изворне ниске" +msgstr "Коментар изворног низа" #: weblate/templates/list-comments.html:17 msgid "Translation comment" @@ -9191,23 +9217,27 @@ msgstr "Генерисано %(timestamp)s." #: weblate/templates/mail/billing_expired.html:7 msgid "Your billing plan has expired and all its projects have been removed." -msgstr "" +msgstr "Ваш план наплате је истекао и сви његови пројекти су уклоњени." #: weblate/templates/mail/billing_expired.html:10 msgid "Your trial period is going to expire in less than a week." -msgstr "" +msgstr "Ваш пробни период истиче за мање од недељу дана." #: weblate/templates/mail/billing_expired.html:12 msgid "" "If you like Weblate and you want to continue using it, please purchase the " "subscription. If not, tell us what you are missing." msgstr "" +"Ако вам се свиђа Веблејт и желите да наставите да га користите, молимо " +"купите претплату. Ако не, реците нам шта вам недостаје." #: weblate/templates/mail/billing_expired.html:14 msgid "" "If you like Weblate and you want to continue using it, just request an " "approval of your libre plan." msgstr "" +"Ако вам се свиђа Веблејт и желите да наставите да га користите, само " +"затражите одобрење вашег либре плана." #: weblate/templates/mail/billing_expired.html:18 msgid "" @@ -9258,7 +9288,7 @@ msgstr "Ваш пројекат превода је заказан за укла #: weblate/templates/mail/billing_expired_subject.txt:7 msgid "Your trial period is about to expire" -msgstr "" +msgstr "Ваш пробни период ускоро истиче" #: weblate/templates/mail/billing_expired_subject.txt:9 msgid "Your billing plan has expired" @@ -9279,7 +9309,7 @@ msgstr "Уреди ову ниску" #: weblate/templates/mail/changed_translation_subject.txt:4 #, python-format msgid "Changed translation in %(translation)s" -msgstr "Измењен превод у %(translation)s" +msgstr "Промењен превод у %(translation)s" #: weblate/templates/mail/changed_translation_subject.txt:6 #, python-format @@ -9293,7 +9323,7 @@ msgstr "Споразум за учесника за %(component)s је изме #: weblate/templates/mail/component_license.html:15 msgid "Old agreement:" -msgstr "" +msgstr "Стари уговор:" #: weblate/templates/mail/component_license.html:18 msgid "There was no contributor agreement before." @@ -9400,7 +9430,7 @@ msgstr "Корисник %(username)s је управо дао свој први #: weblate/templates/mail/new_contributor_subject.txt:3 #, python-format msgid "New contributor in %(translation)s" -msgstr "Нови преводилац у %(translation)s" +msgstr "Нови сарадник у %(translation)s" #: weblate/templates/mail/new_language.html:8 msgid "Added language" @@ -9463,12 +9493,12 @@ msgstr "Предлог" #: weblate/templates/mail/new_suggestion_subject.txt:3 #, python-format msgid "New suggestion in %(translation)s" -msgstr "Нови предлози у %(translation)s" +msgstr "Нова сугестија у %(translation)s" #: weblate/templates/mail/parse_error.html:7 #: weblate/templates/mail/repository_error.html:7 msgid "Error message" -msgstr "Порука грешке" +msgstr "Порука о грешци" #: weblate/templates/mail/parse_error.html:13 msgid "Failing file" @@ -9656,7 +9686,7 @@ msgstr "Честитамо, ваша конфигурација изгледа #: weblate/templates/manage/appearance.html:10 weblate/wladmin/views.py:84 msgid "Appearance" -msgstr "" +msgstr "Изглед" #: weblate/templates/manage/appearance.html:18 msgid "Customize appearance" @@ -9722,7 +9752,7 @@ msgstr "Комерцијални" #: weblate/templates/manage/billing.html:16 msgid "Libre" -msgstr "" +msgstr "Libre" #: weblate/templates/manage/index.html:23 #: weblate/wladmin/templates/admin/weblate-index.html:14 @@ -9892,7 +9922,7 @@ msgstr "" #: weblate/templates/manage/repos.html:6 #: weblate/wladmin/templates/admin/weblate-index.html:20 msgid "Status of repositories" -msgstr "Стање складишта" +msgstr "Статус складишта" #: weblate/templates/manage/repos.html:11 #, fuzzy @@ -9953,7 +9983,7 @@ msgstr "" #: weblate/wladmin/templates/admin/weblate-index.html:26 #: weblate/wladmin/views.py:79 msgid "SSH keys" -msgstr "ССХ кључеви" +msgstr "SSH кључеви" #: weblate/templates/manage/ssh.html:22 #, fuzzy, python-format @@ -9981,11 +10011,11 @@ msgstr "Генериши ССХ кључ" #: weblate/templates/manage/ssh.html:44 msgid "Known host keys" -msgstr "Познати кључеви домаћина" +msgstr "Познати кључеви хоста" #: weblate/templates/manage/ssh.html:51 weblate/wladmin/forms.py:26 msgid "Hostname" -msgstr "Име домаћина" +msgstr "Име хоста" #: weblate/templates/manage/ssh.html:52 msgid "Key type" @@ -9993,11 +10023,11 @@ msgstr "Тип кључа" #: weblate/templates/manage/ssh.html:53 msgid "Fingerprint" -msgstr "Отисак" +msgstr "Отисак прста" #: weblate/templates/manage/ssh.html:70 msgid "Add host key" -msgstr "Додај кључ домаћина" +msgstr "Додај кључ хоста" #: weblate/templates/manage/ssh.html:74 msgid "" @@ -10147,9 +10177,9 @@ msgid "" "This will permanently delete %(count)s entry from the translation memory." msgid_plural "" "This will permanently delete %(count)s entries from the translation memory." -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "Ово ће трајно обрисати %(count)s унос из меморије превода." +msgstr[1] "Ово ће трајно обрисати %(count)s уноса из меморије превода." +msgstr[2] "Ово ће трајно обрисати %(count)s уноса из меморије превода." #: weblate/templates/memory/index.html:71 #: weblate/templates/memory/index.html:87 @@ -10353,7 +10383,7 @@ msgstr "" #: weblate/templates/projects.html:19 msgid "Ask for project hosting" -msgstr "Питајте за хостинг пројекта" +msgstr "Затражите хостинг пројекта" #: weblate/templates/ratelimit.html:5 msgid "" @@ -10511,13 +10541,15 @@ msgstr[2] "%(count)s ниски" #: weblate/templates/snippets/billing-failure.html:10 msgid "This might be caused by missing subscription." -msgstr "" +msgstr "Ово може бити узроковано недостајућом претплатом." #: weblate/templates/snippets/billing-failure.html:13 msgid "" "This might be caused by some of your billings are past due date or over " "limits." msgstr "" +"Ово може бити узроковано тиме што неке од ваших наплата касне или су " +"прекорачиле лимите." #: weblate/templates/snippets/bulk-help.html:3 msgid "You can change the state of all strings matching given type at once." @@ -10619,7 +10651,7 @@ msgstr "" #: weblate/templates/snippets/git-info.html:10 #: weblate/trans/models/component.py:377 msgid "Source code repository" -msgstr "Складиште изворног кôда" +msgstr "Складиште изворног кода" #: weblate/templates/snippets/git-info.html:19 #: weblate/trans/templatetags/translations.py:1072 @@ -10706,7 +10738,7 @@ msgstr "Множине" #: weblate/templates/snippets/info.html:85 weblate/trans/models/project.py:150 msgid "Project website" -msgstr "Веб сајт пројекта" +msgstr "Вебсајт пројекта" #: weblate/templates/snippets/info.html:92 msgid "Instructions for translators" @@ -10720,7 +10752,7 @@ msgstr "Пројекат одржавају" #: weblate/templates/snippets/info.html:151 #: weblate/trans/models/component.py:567 msgid "Translation license" -msgstr "Лиценца превода" +msgstr "Лиценца за превод" #: weblate/templates/snippets/info.html:142 msgid "Automatic component list assignment" @@ -10788,7 +10820,7 @@ msgstr "Фајл превода" #: weblate/templates/snippets/info.html:232 #: weblate/templates/translation.html:409 msgid "Download" -msgstr "Преузимање" +msgstr "Преузми" #: weblate/templates/snippets/info.html:240 msgid "Last change" @@ -10825,7 +10857,7 @@ msgstr "Низови са коментарима" #: weblate/templates/snippets/info.html:520 msgctxt "Number of hosted strings" msgid "Hosted strings" -msgstr "" +msgstr "Хостовани низови" #: weblate/templates/snippets/info.html:291 #, fuzzy @@ -11011,6 +11043,8 @@ msgid "" "Translations have to be in the same repository as the source code, or under " "the same project/organization as the source code repository." msgstr "" +"Преводи морају бити у истом складишту као и изворни код, или под истим " +"пројектом/организацијом као и складиште изворног кода." #: weblate/templates/snippets/libre-basic.html:12 msgid "Non-code projects are welcome as well." @@ -11031,6 +11065,8 @@ msgid "" "Mention you use Weblate for translations in the README. It’ll attract new " "translators and also help Weblate to be free for more projects." msgstr "" +"Наведите да користите Веблејт за преводе у README. То ће привући нове " +"преводиоце и такође помоћи Веблејту да буде бесплатан за више пројеката." #: weblate/templates/snippets/libre-basic.html:21 #, fuzzy @@ -11044,7 +11080,7 @@ msgstr "" #: weblate/templates/snippets/libre-basic.html:24 msgid "The project has had active development for at least three months." -msgstr "" +msgstr "Пројекат је имао активан развој најмање три месеца." #: weblate/templates/snippets/libre-checklist.html:18 msgid "Review guidelines from the community localization checklist." @@ -11100,7 +11136,7 @@ msgstr "Непреведено" #: weblate/templates/translate.html:252 weblate/templates/translate.html:470 #: weblate/trans/forms.py:1548 msgid "Suggestions" -msgstr "Предлози" +msgstr "Сугестије" #: weblate/templates/snippets/list-objects.html:135 #: weblate/templates/translate.html:258 weblate/templates/translate.html:536 @@ -11184,7 +11220,7 @@ msgstr "" #: weblate/templates/snippets/position-field.html:8 #: weblate/templates/snippets/position-field.html:11 msgid "First" -msgstr "Прва" +msgstr "Први" #: weblate/templates/snippets/position-field.html:17 #, python-format @@ -11207,7 +11243,7 @@ msgstr "/ %(filter_count)s" #: weblate/templates/snippets/position-field.html:31 #: weblate/templates/snippets/position-field.html:34 msgid "Last" -msgstr "Последња" +msgstr "Последњи" #: weblate/templates/snippets/progress.html:3 #, fuzzy @@ -11379,7 +11415,7 @@ msgstr "" #: weblate/templates/snippets/share-menu.html:6 msgid "Share" -msgstr "Дели" +msgstr "Подели" #: weblate/templates/snippets/share-menu.html:10 msgid "Share on Facebook" @@ -11619,13 +11655,15 @@ msgstr "" #: weblate/templates/trans/alert/ambiguouslanguage.html:13 msgid "Suggested individual language codes" -msgstr "" +msgstr "Предложени појединачни језички кодови" #: weblate/templates/trans/alert/billinglimit.html:2 msgid "" "Your billing plan has exceeded its limits. You can still use the service, " "but are recommended to upgrade the billing plan." msgstr "" +"Ваш план наплате је премашио своје лимите. И даље можете користити услугу, " +"али се препоручује да надоградите план наплате." #: weblate/templates/trans/alert/brokenbrowserurl.html:2 #: weblate/templates/trans/alert/inexistantfiles.html:2 @@ -12156,6 +12194,8 @@ msgid "" "This action cannot be undone. This will permanently delete %(object)s " "translations and all related content." msgstr "" +"Ова акција се не може поништити. Ово ће трајно обрисати %(object)s преводе и " +"сав повезани садржај." #: weblate/templates/trans/delete-category-language.html:6 #: weblate/templates/trans/delete-project-language.html:6 @@ -12493,7 +12533,7 @@ msgstr "Термин појмовника" #: weblate/templates/translate.html:106 weblate/templates/zen-units.html:67 msgid "Source change" -msgstr "Измена извора" +msgstr "Промена извора" #: weblate/templates/translate.html:122 msgid "Intermediate language" @@ -12587,7 +12627,7 @@ msgstr "Други језици" #: weblate/templates/translate.html:264 msgid "List of recent changes done in Weblate" -msgstr "Списак последњих измена одрађених у Веблејту" +msgstr "Листа недавних промена у Веблејту" #: weblate/templates/translate.html:299 msgid "Showing only subset of the strings as there were too many matches." @@ -13415,7 +13455,7 @@ msgstr "Остале компоненте превода" #: weblate/trans/forms.py:928 msgid "Machine translation" -msgstr "Машинско превођење" +msgstr "Машински превод" #: weblate/trans/forms.py:936 msgid "" @@ -13777,7 +13817,7 @@ msgstr "Унесите пуни назив пројекта и језика да #: weblate/trans/forms.py:2758 msgid "Action" -msgstr "Радња" +msgstr "Акција" #: weblate/trans/forms.py:2764 msgid "Author username" @@ -14837,6 +14877,7 @@ msgstr "Гласање за предлоге" #: weblate/trans/models/component.py:536 weblate/trans/models/workflow.py:38 msgid "Users can only vote for suggestions and can’t make direct translations." msgstr "" +"Корисници могу само гласати за предлоге и не могу правити директне преводе." #: weblate/trans/models/component.py:541 weblate/trans/models/workflow.py:43 #, fuzzy @@ -15181,6 +15222,8 @@ msgid "" "An intermediate language file has to be different from monolingual base " "language file. You can probably keep it empty." msgstr "" +"Интермедијарни језички фајл мора бити различит од једнојезичног базног " +"језичког фајла. Вероватно га можете оставити празним." #: weblate/trans/models/component.py:2926 msgid "Using a .pot file as base file is unsupported." @@ -15194,7 +15237,7 @@ msgstr "Нисмо могли да парсирамо базну датотек #: weblate/trans/models/component.py:2953 #, python-brace-format msgid "Template language ({0}) does not match source language ({1})!" -msgstr "" +msgstr "Језик шаблона ({0}) не одговара изворном језику ({1})!" #: weblate/trans/models/component.py:2972 #, fuzzy @@ -15213,6 +15256,8 @@ msgid "" "Your push URL seems to miss credentials. Either provide them in the URL or " "use SSH with key based authentication." msgstr "" +"Чини се да ваш push URL недостају акредитиви. Или их обезбедите у URL-у или " +"користите SSH са аутентификацијом на основу кључа." #: weblate/trans/models/component.py:2996 #, python-format @@ -15231,6 +15276,8 @@ msgstr "" msgid "" "Source language can not be changed, please recreate the component instead." msgstr "" +"Изворни језик не може бити промењен, молимо поново креирајте компоненту " +"уместо тога." #: weblate/trans/models/component.py:3071 msgid "Can not validate file matches due to invalid regular expression." @@ -15434,6 +15481,8 @@ msgid "" "Comma-separated list of language code mappings, for example: en_GB:en,en_US:" "en" msgstr "" +"Листа мапирања језичких кодова одвојених зарезом, на пример: " +"en_GB:en,en_US:en" #: weblate/trans/models/suggestion.py:155 #, fuzzy @@ -16080,7 +16129,7 @@ msgstr "Измене у језику %s" #: weblate/trans/views/create.py:281 #, python-format msgid "Detected license as %s, please check whether it is correct." -msgstr "" +msgstr "Откривена лиценца као %s, молимо проверите да ли је исправна." #: weblate/trans/views/create.py:396 #, fuzzy @@ -16726,7 +16775,7 @@ msgstr "Овај назив је забрањен" #: weblate/utils/validators.py:270 msgid "Syntax error in language aliases." -msgstr "" +msgstr "Синтаксна грешка у алијасима језика." #: weblate/utils/validators.py:286 weblate/utils/validators.py:295 #: weblate/utils/validators.py:304 @@ -16934,7 +16983,7 @@ msgstr "Пробна е-пошта ће бити послата на ову ад #: weblate/wladmin/forms.py:57 msgid "Please provide font family suitable for CSS." -msgstr "" +msgstr "Молимо обезбедите породицу фонтова погодну за CSS." #: weblate/wladmin/forms.py:68 msgid "Page font" @@ -16946,25 +16995,27 @@ msgstr "Фонт заглавља" #: weblate/wladmin/forms.py:80 msgid "Focus color" -msgstr "" +msgstr "Боја фокуса" #: weblate/wladmin/forms.py:83 msgid "Hover color" -msgstr "" +msgstr "Боја при преласку мишем" #: weblate/wladmin/forms.py:86 msgid "Hide page footer" -msgstr "" +msgstr "Сакриј подножје странице" #: weblate/wladmin/forms.py:89 msgid "Always show hamburger menu" -msgstr "" +msgstr "Увек приказуј мени са хамбургером" #: weblate/wladmin/forms.py:92 msgid "" "Persistent navigational drop-down menu in the top right corner, even if " "there is room for a full menu." msgstr "" +"Перзистентни навигациони падајући мени у горњем десном углу, чак и ако има " +"простора за пуни мени." #: weblate/wladmin/models.py:123 msgid "Community support" From 41f05d65f0b36b18c5a7ffc22240697283ff93c8 Mon Sep 17 00:00:00 2001 From: Fjuro Date: Tue, 1 Oct 2024 11:56:18 +0000 Subject: [PATCH 44/61] Translated using Weblate (Czech) Currently translated at 15.0% (1445 of 9608 strings) Translation: Weblate/Documentation Translate-URL: https://hosted.weblate.org/projects/weblate/documentation/cs/ --- docs/locales/cs/LC_MESSAGES/docs.po | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/locales/cs/LC_MESSAGES/docs.po b/docs/locales/cs/LC_MESSAGES/docs.po index c9c2a1cc54c6..4128d5da21b5 100644 --- a/docs/locales/cs/LC_MESSAGES/docs.po +++ b/docs/locales/cs/LC_MESSAGES/docs.po @@ -12,7 +12,7 @@ msgstr "" "Project-Id-Version: Weblate 5.8\n" "Report-Msgid-Bugs-To: https://github.com/WeblateOrg/weblate/issues\n" "POT-Creation-Date: 2024-09-20 14:27+0200\n" -"PO-Revision-Date: 2024-09-24 08:31+0000\n" +"PO-Revision-Date: 2024-10-01 17:34+0000\n" "Last-Translator: Fjuro \n" "Language-Team: Czech \n" @@ -45915,8 +45915,8 @@ msgid "" "wherever your user profile appears." msgstr "" "Všechny položky na této stránce jsou volitelné a mohou být kdykoliv " -"odstraněny. Jejich vyplněním nám dáváte souhla sdílet tyto údaje kdykoliv se " -"zobrazí váš veřejný profil." +"odstraněny. Jejich vyplněním nám dáváte souhlas sdílet tyto údaje kdekoliv, " +"kde je vidět váš veřejný profil." #: ../../user/profile.rst:219 msgid "" From 0528fe7d45b230c5fafa53f866b72ee9c19cb81e Mon Sep 17 00:00:00 2001 From: Tuomas Hietala Date: Tue, 1 Oct 2024 09:55:00 +0000 Subject: [PATCH 45/61] Translated using Weblate (Finnish) Currently translated at 23.8% (2287 of 9608 strings) Translation: Weblate/Documentation Translate-URL: https://hosted.weblate.org/projects/weblate/documentation/fi/ --- docs/locales/fi/LC_MESSAGES/docs.po | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/docs/locales/fi/LC_MESSAGES/docs.po b/docs/locales/fi/LC_MESSAGES/docs.po index 3d272739b829..1437741a909e 100644 --- a/docs/locales/fi/LC_MESSAGES/docs.po +++ b/docs/locales/fi/LC_MESSAGES/docs.po @@ -10,8 +10,8 @@ msgstr "" "Project-Id-Version: Weblate 5.8\n" "Report-Msgid-Bugs-To: https://github.com/WeblateOrg/weblate/issues\n" "POT-Creation-Date: 2024-09-20 14:27+0200\n" -"PO-Revision-Date: 2024-09-27 14:15+0000\n" -"Last-Translator: Ricky Tigg \n" +"PO-Revision-Date: 2024-10-01 17:34+0000\n" +"Last-Translator: Tuomas Hietala \n" "Language-Team: Finnish \n" "Language: fi\n" @@ -15671,15 +15671,10 @@ msgid "Translation context" msgstr "" #: ../../admin/machine.rst:148 -#, fuzzy -#| msgid "" -#| "Weblate could not parse the translation files while updating the " -#| "translations." msgid "" "Describe the context of the translation to improve the accuracy of the " "translation." -msgstr "" -"Weblate ei voi jäsentää käännöstiedostoja käännösten päivittämisen aikana." +msgstr "Kuvaile käännöksen asiayhteyttä käännöksen tarkkuuden parantamiseksi." #: ../../admin/machine.rst:151 msgid "" From d76a11ce5b7d137b87f3d567245c5760d7feeffe Mon Sep 17 00:00:00 2001 From: Reno Tx Date: Mon, 30 Sep 2024 22:38:21 +0000 Subject: [PATCH 46/61] Translated using Weblate (Serbian) Currently translated at 11.7% (1131 of 9608 strings) Translation: Weblate/Documentation Translate-URL: https://hosted.weblate.org/projects/weblate/documentation/sr/ --- docs/locales/sr/LC_MESSAGES/docs.po | 41 ++++++++++++++++------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/docs/locales/sr/LC_MESSAGES/docs.po b/docs/locales/sr/LC_MESSAGES/docs.po index 89a55aaced11..ca42f91e9b75 100644 --- a/docs/locales/sr/LC_MESSAGES/docs.po +++ b/docs/locales/sr/LC_MESSAGES/docs.po @@ -12,7 +12,7 @@ msgstr "" "Project-Id-Version: Weblate 5.8\n" "Report-Msgid-Bugs-To: https://github.com/WeblateOrg/weblate/issues\n" "POT-Creation-Date: 2024-09-20 14:27+0200\n" -"PO-Revision-Date: 2024-09-30 13:16+0000\n" +"PO-Revision-Date: 2024-10-01 17:34+0000\n" "Last-Translator: Reno Tx \n" "Language-Team: Serbian \n" @@ -1133,7 +1133,7 @@ msgstr "" #: ../../admin/access.rst:535 ../../user/translating.rst:44 #: ../../workflows.rst:83 msgid "Suggestions" -msgstr "Предлози" +msgstr "Сугестије" #: ../../admin/access.rst:535 msgid "Accept suggestion" @@ -1297,7 +1297,7 @@ msgstr "Управља корисницима" #: ../../admin/access.rst:581 msgid "Manage users" -msgstr "Управља корисницима" +msgstr "Управљање корисницима" #: ../../admin/access.rst:583 msgid "Manage roles" @@ -1977,6 +1977,8 @@ msgid "" "Publishes translations into content delivery network for use in JavaScript " "or HTML localization." msgstr "" +"Објављује преводе у мрежу за испоруку садржаја за употребу у JavaScript или " +"HTML локализацији." #: ../../admin/addons.rst:98 msgid "" @@ -17918,7 +17920,7 @@ msgstr "" #: ../../admin/optionals.rst:285 msgid "Name" -msgstr "Назив" +msgstr "Име" #: ../../admin/optionals.rst:285 msgid "Allowed attempts" @@ -18277,7 +18279,7 @@ msgstr "" #: ../../admin/projects.rst:124 ../../admin/projects.rst:1042 ../../api.rst:829 #: ../../contributing/about.rst:11 msgid "Project name" -msgstr "Назив пројекта" +msgstr "Име пројекта" #: ../../admin/projects.rst:126 msgid "Verbose project name, used to display the project name." @@ -18298,7 +18300,7 @@ msgstr "" #: ../../admin/projects.rst:142 ../../api.rst:833 #: ../../contributing/about.rst:16 msgid "Project website" -msgstr "Веб сајт пројекта" +msgstr "Вебсајт пројекта" #: ../../admin/projects.rst:144 msgid "URL where translators can find more info about the project." @@ -18555,7 +18557,7 @@ msgstr ":ref:`push-changes`" #: ../../admin/projects.rst:345 ../../devel/gettext.rst:224 msgid "Source code repository" -msgstr "Складиште изворног кôда" +msgstr "Складиште изворног кода" #: ../../admin/projects.rst:347 msgid "VCS repository used to pull changes." @@ -19003,7 +19005,7 @@ msgstr "" #: ../../admin/projects.rst:647 msgid "Translation license" -msgstr "Лиценца превода" +msgstr "Лиценца за превод" #: ../../admin/projects.rst:649 msgid "" @@ -23189,7 +23191,7 @@ msgstr "Подеси уређивач" #: ../../api.rst:2619 msgid "Search" -msgstr "Тражи" +msgstr "Претрага" #: ../../api.rst:2625 msgid "" @@ -23527,7 +23529,7 @@ msgstr "" #: ../../api.rst:2933 msgid "RSS feeds" -msgstr "РСС доводи" +msgstr "RSS фидови" #: ../../api.rst:2935 msgid "Changes in translations are exported in RSS feeds." @@ -36365,7 +36367,7 @@ msgstr "" #: ../../devel/community.rst:27 msgid "Machine translation" -msgstr "Машинско превођење" +msgstr "Машински превод" #: ../../devel/community.rst:28 msgid "" @@ -43138,11 +43140,11 @@ msgstr "" #: ../../user/checks.rst:888 msgid "Vue I18n formatting" -msgstr "" +msgstr "Vue I18n форматирање" #: ../../user/checks.rst:890 msgid "The Vue I18n formatting does not match source" -msgstr "" +msgstr "Vue I18n форматирање не одговара извору" #: ../../user/checks.rst:892 msgid "``weblate.checks.format.VueFormattingCheck``" @@ -44078,7 +44080,7 @@ msgstr "" #: ../../user/checks.rst:1535 msgid "URL" -msgstr "УРЛ" +msgstr "URL" #: ../../user/checks.rst:1537 msgid "The translation does not contain an URL" @@ -45341,7 +45343,7 @@ msgstr "Изаберите језик на који желите да прево #: ../../user/profile.rst:86 msgid "Translated languages" -msgstr "Језици превода" +msgstr "Преведени језици" #: ../../user/profile.rst:88 msgid "" @@ -45363,7 +45365,7 @@ msgstr "" #: ../../user/profile.rst:108 msgid "Preferences" -msgstr "Подешавања" +msgstr "Преференце" #: ../../user/profile.rst:111 msgid "Theme" @@ -45429,7 +45431,7 @@ msgstr "" #: ../../user/profile.rst:155 msgid "Notifications" -msgstr "Обавештења" +msgstr "Нотификације" #: ../../user/profile.rst:157 msgid "" @@ -45509,6 +45511,9 @@ msgid "" "and by filling them out, you're giving us consent to share this data " "wherever your user profile appears." msgstr "" +"Сва поља на овој страници су опционална и могу се обрисати у било ком " +"тренутку, а попуњавањем истих, дајете нам сагласност да делимо ове податке " +"где год се појављује ваш кориснички профил." #: ../../user/profile.rst:219 msgid "" @@ -48290,7 +48295,7 @@ msgstr "" #: ../../workflows.rst:110 ../../workflows.rst:142 ../../workflows.rst:175 msgid "Note" -msgstr "Белешка" +msgstr "Напомена" #: ../../workflows.rst:112 ../../workflows.rst:117 ../../workflows.rst:144 #: ../../workflows.rst:148 ../../workflows.rst:179 ../../workflows.rst:182 From 14ed62d80e168551d7c46a6d0e33aa520d6d5648 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 17:02:53 +0000 Subject: [PATCH 47/61] chore(deps): update dependency @biomejs/biome to v1.9.3 --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b5ebdb2f146b..0222219fa883 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -78,7 +78,7 @@ repos: hooks: - id: biome-check additional_dependencies: - - '@biomejs/biome@1.9.2' + - '@biomejs/biome@1.9.3' - repo: https://github.com/abravalheri/validate-pyproject rev: v0.20.2 hooks: From 29306e229ea18d44208ee30048792dbb8999eaa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Harriet=20Asi=C3=B1ero?= Date: Wed, 2 Oct 2024 02:12:04 +0800 Subject: [PATCH 48/61] feat(component): Add component filtering based on key (#12032) * Added key_filter field in Component model * Added key filter in Component setting form and implemented key filtering in Translation.check_sync * Applied changes based on comment and mitigated migration conflict * Improved the disable for key_filted in the UI and the filtering condition * Added tests and drop_key_filter_cache() * Improved the implementation of firing check_sync when saving and added a test * Updated code based on suggestions * Updated the validation error message Co-authored-by: Benjamin Alan Jamie --- docs/admin/projects.rst | 15 ++++++ docs/changes.rst | 1 + weblate/api/serializers.py | 1 + weblate/trans/forms.py | 13 ++++++ .../migrations/0024_component_key_filter.py | 29 ++++++++++++ weblate/trans/models/component.py | 30 ++++++++++++ weblate/trans/models/translation.py | 12 +++++ weblate/trans/tests/test_component.py | 46 +++++++++++++++++++ 8 files changed, 147 insertions(+) create mode 100644 weblate/trans/migrations/0024_component_key_filter.py diff --git a/docs/admin/projects.rst b/docs/admin/projects.rst index a14322bbfdc1..622eb1fd8013 100644 --- a/docs/admin/projects.rst +++ b/docs/admin/projects.rst @@ -910,6 +910,21 @@ Some examples of filtering: | Include all files (default) | ``^[^.]+$`` | +-------------------------------+-----------------------+ + +.. _component-key_filter: + +Key filter +++++++++++ + +A regular expression that is used to filter units by their keys. It displays only +those units whose keys match the regular expression that was set +as the value of this field. + +.. note:: + + This filter is only available for components with monolingual file formats. + + .. _component-variant_regex: Variants regular expression diff --git a/docs/changes.rst b/docs/changes.rst index bea7f3adee15..daea2dde8e43 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -5,6 +5,7 @@ Not yet released. **New features** +* Added :ref:`component-key_filter` in the component. * :ref:`Searching` now supports filtering by object path and :ref:`date-search`. * Merge requests credentials can now be passed in the repository URL, see :ref:`settings-credentials`. * :ref:`mt-azure-openai` automatic suggestion service. diff --git a/weblate/api/serializers.py b/weblate/api/serializers.py index 43b06eb07053..115cd09810f3 100644 --- a/weblate/api/serializers.py +++ b/weblate/api/serializers.py @@ -585,6 +585,7 @@ class Meta: "commit_pending_age", "auto_lock_error", "language_regex", + "key_filter", "variant_regex", "zipfile", "docfile", diff --git a/weblate/trans/forms.py b/weblate/trans/forms.py index 67e79b107917..68b882a01a73 100644 --- a/weblate/trans/forms.py +++ b/weblate/trans/forms.py @@ -1491,6 +1491,7 @@ class Meta: "template", "intermediate", "language_regex", + "key_filter", "variant_regex", "restricted", "auto_lock_error", @@ -1513,6 +1514,7 @@ def __init__(self, request: AuthenticatedHttpRequest, *args, **kwargs) -> None: self.fields["links"].queryset = request.user.managed_projects.exclude( pk=self.instance.project.pk ) + self.helper.layout = Layout( TabHolder( Tab( @@ -1603,6 +1605,7 @@ def __init__(self, request: AuthenticatedHttpRequest, *args, **kwargs) -> None: "file_format", "filemask", "language_regex", + "key_filter", "source_language", ), Fieldset( @@ -1650,6 +1653,15 @@ def clean(self) -> None: if self.hide_restricted: data["restricted"] = self.instance.restricted + if ( + self.instance + and self.instance.key_filter + and not self.instance.file_format_cls.monolingual + ): + raise ValidationError( + gettext("To use the key filter, the file format must be monolingual.") + ) + class ComponentCreateForm(SettingsBaseForm, ComponentDocsMixin, ComponentAntispamMixin): """Component creation form.""" @@ -1677,6 +1689,7 @@ class Meta: "license", "language_code_style", "language_regex", + "key_filter", "source_language", "is_glossary", ] diff --git a/weblate/trans/migrations/0024_component_key_filter.py b/weblate/trans/migrations/0024_component_key_filter.py new file mode 100644 index 000000000000..631000241545 --- /dev/null +++ b/weblate/trans/migrations/0024_component_key_filter.py @@ -0,0 +1,29 @@ +# Copyright © Michal Čihař +# +# SPDX-License-Identifier: GPL-3.0-or-later + +# Generated by Django 5.1.1 on 2024-09-17 09:47 + +from django.db import migrations + +import weblate.trans.fields + + +class Migration(migrations.Migration): + dependencies = [ + ("trans", "0023_alter_label_description"), + ] + + operations = [ + migrations.AddField( + model_name="component", + name="key_filter", + field=weblate.trans.fields.RegexField( + blank=True, + default="", + help_text="Regular expression used to filter keys. This is only available for monolingual formats.", + max_length=500, + verbose_name="Key filter", + ), + ), + ] diff --git a/weblate/trans/models/component.py b/weblate/trans/models/component.py index 272d99bc11e8..144dc05a4aed 100644 --- a/weblate/trans/models/component.py +++ b/weblate/trans/models/component.py @@ -770,6 +770,16 @@ class Component(models.Model, PathMixin, CacheKeyMixin, ComponentCategoryMixin): local_revision = models.CharField(max_length=200, default="", blank=True) processed_revision = models.CharField(max_length=200, default="", blank=True) + key_filter = RegexField( + verbose_name=gettext_lazy("Key filter"), + max_length=500, + default="", + help_text=gettext_lazy( + "Regular expression used to filter keys. This is only available for monolingual formats." + ), + blank=True, + ) + objects = ComponentQuerySet.as_manager() is_lockable = True @@ -818,6 +828,11 @@ def save(self, *args, **kwargs) -> None: changed_template = False changed_variant = False create = True + + # Sets the key_filter to blank if the file format is bilingual + if self.key_filter and not self.file_format_cls.monolingual: + self.key_filter = "" + if self.id: old = Component.objects.get(pk=self.id) changed_git = ( @@ -835,9 +850,13 @@ def save(self, *args, **kwargs) -> None: or (old.edit_template != self.edit_template) or (old.new_base != self.new_base) or changed_template + or old.key_filter != self.key_filter ) if changed_setup: old.commit_pending("changed setup", None) + if old.key_filter != self.key_filter: + self.drop_key_filter_cache() + changed_variant = old.variant_regex != self.variant_regex # Generate change entries for changes self.generate_changes(old) @@ -852,6 +871,7 @@ def save(self, *args, **kwargs) -> None: old.component_set.update(repo=self.get_repo_link_url()) if changed_git: self.drop_repository_cache() + create = False elif self.is_glossary: # Creating new glossary @@ -3386,6 +3406,11 @@ def drop_addons_cache(self) -> None: if "addons_cache" in self.__dict__: del self.__dict__["addons_cache"] + def drop_key_filter_cache(self) -> None: + """Invalidate the cached value of key_filter.""" + if "key_filter_re" in self.__dict__: + del self.__dict__["key_filter_re"] + def load_intermediate_store(self): """Load translate-toolkit store for intermediate.""" store = self.file_format_cls( @@ -3793,6 +3818,11 @@ def all_repo_components(self): def start_sentry_span(self, op: str): return sentry_sdk.start_span(op=op, description=self.full_slug) + @cached_property + def key_filter_re(self) -> re.Pattern: + """Provide the cached version of key_filter.""" + return re.compile(self.key_filter) + @receiver(m2m_changed, sender=Component.links.through) @disable_for_loaddata diff --git a/weblate/trans/models/translation.py b/weblate/trans/models/translation.py index 1912883f19a9..cf13c3f5bfb7 100644 --- a/weblate/trans/models/translation.py +++ b/weblate/trans/models/translation.py @@ -437,6 +437,18 @@ def check_sync(self, force=False, request=None, change=None) -> None: # noqa: C unit.source = translated_unit.source except UnitNotFoundError: pass + if ( + self.component.file_format_cls.monolingual + and self.component.key_filter_re + and self.component.key_filter_re.match(unit.context) is None + ): + # This is where the key filtering take place + self.log_info( + "Doesn't match with key_filter, skipping: %s (%s)", + unit.context, + repr(unit.source), + ) + continue try: id_hash = unit.id_hash diff --git a/weblate/trans/tests/test_component.py b/weblate/trans/tests/test_component.py index 785b8b9b8f9c..042c88ce3cc1 100644 --- a/weblate/trans/tests/test_component.py +++ b/weblate/trans/tests/test_component.py @@ -1058,3 +1058,49 @@ def test_readonly(self) -> None: # It should be now read only self.assertEqual(source.unit_set.all()[0].state, STATE_READONLY) + + +class ComponentKeyFilterTest(ViewTestCase): + """Test the key filtering implementation in Component.""" + + def create_component(self): + return self.create_android(key_filter=r"^tr") + + def test_get_key_filter_re(self) -> None: + self.assertEqual(self.component.key_filter_re.pattern, "^tr") + + def test_get_filtered_result(self) -> None: + translation = self.component.translation_set.get(language_code="en") + units = translation.unit_set.all() + self.assertEqual(units.count(), 1) + self.assertEqual(units.all()[0].context, "try") + + def test_change_key_filter(self) -> None: + self.component.key_filter = r"^th" + self.component.save() + self.assertEqual(self.component.key_filter_re.pattern, "^th") + translations = self.component.translation_set.all() + for translation in translations: + units = translation.unit_set.all() + self.assertEqual(units.count(), 1) + self.assertEqual(units.all()[0].context, "thanks") + + self.component.key_filter = "" + self.component.save() + self.assertEqual(self.component.key_filter_re.pattern, "") + translations = self.component.translation_set.all() + for translation in translations: + units = translation.unit_set.all() + self.assertEqual(len(units), 4) + + def test_bilingual_component(self): + project = self.component.project + component = self.create_po( + name="Bilingual Test", project=project, key_filter=r"^tr" + ) + self.assertRaisesMessage( + ValidationError, + "To use the key filter, the file format must be monolingual.", + ) + self.assertEqual(component.key_filter, "") + self.assertEqual(component.key_filter_re.pattern, "") From 7bf459fe72c7d88d8371f64ffdbc62f2b3195d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 1 Oct 2024 20:13:03 +0200 Subject: [PATCH 49/61] docs: annotate version added for #12032 --- docs/admin/projects.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/admin/projects.rst b/docs/admin/projects.rst index 622eb1fd8013..2cf4abb3f181 100644 --- a/docs/admin/projects.rst +++ b/docs/admin/projects.rst @@ -916,6 +916,8 @@ Some examples of filtering: Key filter ++++++++++ +.. versionadded:: 5.8 + A regular expression that is used to filter units by their keys. It displays only those units whose keys match the regular expression that was set as the value of this field. From 98cd30c3353daf0526ed3b4f150f0b0be6169868 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 22:18:57 +0000 Subject: [PATCH 50/61] chore(deps): update dependency boto3-stubs to v1.35.31 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 43e34c0ffbbc..1e276ef71bf8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -144,7 +144,7 @@ mercurial = [ "mercurial>=6.8.0,<7.0" ] mypy = [ - "boto3-stubs==1.35.30", + "boto3-stubs==1.35.31", "celery-types==0.22.0", "django-stubs-ext==5.1.0", "django-stubs==5.1.0", From f66fe69588f09376e536d8552dc1e15ceb29ca46 Mon Sep 17 00:00:00 2001 From: "Miguel A. Bouzada" Date: Tue, 1 Oct 2024 22:29:41 +0000 Subject: [PATCH 51/61] Translated using Weblate (Galician) Currently translated at 100.0% (3223 of 3223 strings) Translation: Weblate/Application Translate-URL: https://hosted.weblate.org/projects/weblate/application/gl/ --- weblate/locale/gl/LC_MESSAGES/django.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weblate/locale/gl/LC_MESSAGES/django.po b/weblate/locale/gl/LC_MESSAGES/django.po index ed550b9bdbc1..5a85293a611a 100644 --- a/weblate/locale/gl/LC_MESSAGES/django.po +++ b/weblate/locale/gl/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: Weblate 5.8\n" "Report-Msgid-Bugs-To: https://github.com/WeblateOrg/weblate/issues\n" "POT-Creation-Date: 2024-09-20 12:26+0000\n" -"PO-Revision-Date: 2024-09-27 07:38+0000\n" +"PO-Revision-Date: 2024-10-02 07:34+0000\n" "Last-Translator: \"Miguel A. Bouzada\" \n" "Language-Team: Galician \n" From 90e50b9cafe174ebdef3df6d20ddd4849dec4a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Wed, 2 Oct 2024 11:35:39 +0200 Subject: [PATCH 52/61] fix(templates): fix generating link to contributors list This got recently broken while converting this template to template tag. Fixes #12637 --- weblate/templates/snippets/info.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weblate/templates/snippets/info.html b/weblate/templates/snippets/info.html index fe7e6fa8572c..64b43a34ec74 100644 --- a/weblate/templates/snippets/info.html +++ b/weblate/templates/snippets/info.html @@ -491,7 +491,7 @@

    {% trans "Trends of last 30 days" %}

    - +
    {{ metrics.contributors|number_format }}
    {% trans "Contributors" %}
    From c779db09620b92f14d31b202ecdf5d92d550d327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Wed, 2 Oct 2024 11:59:44 +0200 Subject: [PATCH 53/61] chore: fix type annotations for spectacular settings --- weblate/api/spectacular.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/weblate/api/spectacular.py b/weblate/api/spectacular.py index fc663effc4e8..b9a20af01864 100644 --- a/weblate/api/spectacular.py +++ b/weblate/api/spectacular.py @@ -4,12 +4,14 @@ from __future__ import annotations +from typing import Any + from django.utils.translation import gettext_lazy def get_spectacular_settings( installed_apps: list[str], site_url: str, site_title: str -) -> None: +) -> dict[str, Any]: settings = { # Use redoc from sidecar # TODO: Should bundle it internally From e23f3906bffda4e02f349fbb1e9a0ef29cf6d531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Wed, 2 Oct 2024 10:54:49 +0200 Subject: [PATCH 54/61] chore(sentry): update start_span to use name istead of description arg This was introduced in https://github.com/getsentry/sentry-python/pull/3524 and description immediatelly started to throw depreciation error. --- pyproject.toml | 2 +- weblate/addons/models.py | 4 +--- weblate/auth/models.py | 2 +- weblate/checks/base.py | 2 +- weblate/glossary/models.py | 4 ++-- weblate/screenshots/views.py | 8 ++++---- weblate/trans/models/component.py | 2 +- weblate/trans/models/translation.py | 6 +++--- weblate/trans/views/edit.py | 4 ++-- weblate/utils/lock.py | 2 +- weblate/utils/stats.py | 4 +--- weblate/vcs/git.py | 2 +- 12 files changed, 19 insertions(+), 23 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1e276ef71bf8..1030f90b165d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,7 +76,7 @@ dependencies = [ "redis>=5.0.2,<5.2.0", "requests>=2.32.2,<2.33", "ruamel.yaml>=0.17.2,<0.19.0", - "sentry-sdk>=2.8,<3.0", + "sentry-sdk>=2.15.0,<3.0", "siphashc>=2.1,<3.0", "social-auth-app-django>=5.4.1,<6.0.0", "social-auth-core>=4.5.0,<5.0.0", diff --git a/weblate/addons/models.py b/weblate/addons/models.py index 21b2a38e4081..68a9db2fb413 100644 --- a/weblate/addons/models.py +++ b/weblate/addons/models.py @@ -302,9 +302,7 @@ def execute_addon_event( try: # Execute event in senty span to track performance - with sentry_sdk.start_span( - op=f"addon.{event.name}", description=addon.name - ): + with sentry_sdk.start_span(op=f"addon.{event.name}", name=addon.name): if isinstance(method, str): log_result = getattr(addon.addon, method)(*args) else: diff --git a/weblate/auth/models.py b/weblate/auth/models.py index 6f1a405e5f6b..5e21f6fc6aeb 100644 --- a/weblate/auth/models.py +++ b/weblate/auth/models.py @@ -726,7 +726,7 @@ def _fetch_permissions(self) -> None: """Fetch all user permissions into a dictionary.""" projects: PermissionCacheType = defaultdict(list) components: SimplePermissionCacheType = defaultdict(list) - with sentry_sdk.start_span(op="permissions", description=self.username): + with sentry_sdk.start_span(op="permissions", name=self.username): for group in self.cached_groups: # Skip permissions for not verified users if group.enforced_2fa and not self.profile.has_2fa: diff --git a/weblate/checks/base.py b/weblate/checks/base.py index dd12684f5590..cc7913e362b8 100644 --- a/weblate/checks/base.py +++ b/weblate/checks/base.py @@ -216,7 +216,7 @@ def check_component(self, component: Component) -> Iterable[Unit]: raise NotImplementedError def perform_batch(self, component: Component) -> None: - with sentry_sdk.start_span(op="check.perform_batch", description=self.check_id): + with sentry_sdk.start_span(op="check.perform_batch", name=self.check_id): self._perform_batch(component) def _perform_batch(self, component: Component) -> None: diff --git a/weblate/glossary/models.py b/weblate/glossary/models.py index e793e0f20a50..00d87f4e5c83 100644 --- a/weblate/glossary/models.py +++ b/weblate/glossary/models.py @@ -45,7 +45,7 @@ def get_glossary_sources(component): def get_glossary_automaton(project): from weblate.trans.models.component import prefetch_glossary_terms - with sentry_sdk.start_span(op="glossary.automaton", description=project.slug): + with sentry_sdk.start_span(op="glossary.automaton", name=project.slug): # Chain terms prefetch_glossary_terms(project.glossaries) terms = set( @@ -107,7 +107,7 @@ def get_glossary_terms( automaton = project.glossary_automaton positions: dict[str, list[tuple[int, int]]] = defaultdict(list) # Extract terms present in the source - with sentry_sdk.start_span(op="glossary.match", description=project.slug): + with sentry_sdk.start_span(op="glossary.match", name=project.slug): for _termno, start, end in automaton.find_matches_as_indexes( source, overlapping=True ): diff --git a/weblate/screenshots/views.py b/weblate/screenshots/views.py index 659bb4b10ac1..046317c8e4a0 100644 --- a/weblate/screenshots/views.py +++ b/weblate/screenshots/views.py @@ -177,7 +177,7 @@ def ensure_tesseract_language(lang: str) -> None: LOGGER.debug("downloading tesseract data %s", url) - with sentry_sdk.start_span(op="ocr.download", description=url): + with sentry_sdk.start_span(op="ocr.download", name=url): response = request("GET", url, allow_redirects=True) with open(full_name, "xb") as handle: @@ -384,14 +384,14 @@ def ocr_get_strings(api, image: str, resolution: int = 72): else: api.SetSourceResolution(resolution) - with sentry_sdk.start_span(op="ocr.recognize", description=image): + with sentry_sdk.start_span(op="ocr.recognize", name=image): api.Recognize() - with sentry_sdk.start_span(op="ocr.iterate", description=image): + with sentry_sdk.start_span(op="ocr.iterate", name=image): iterator = api.GetIterator() level = RIL.TEXTLINE for r in iterate_level(iterator, level): - with sentry_sdk.start_span(op="ocr.text", description=image): + with sentry_sdk.start_span(op="ocr.text", name=image): try: yield r.GetUTF8Text(level) except RuntimeError: diff --git a/weblate/trans/models/component.py b/weblate/trans/models/component.py index 144dc05a4aed..5dd1b7e50fbe 100644 --- a/weblate/trans/models/component.py +++ b/weblate/trans/models/component.py @@ -3816,7 +3816,7 @@ def all_repo_components(self): return [self] def start_sentry_span(self, op: str): - return sentry_sdk.start_span(op=op, description=self.full_slug) + return sentry_sdk.start_span(op=op, name=self.full_slug) @cached_property def key_filter_re(self) -> re.Pattern: diff --git a/weblate/trans/models/translation.py b/weblate/trans/models/translation.py index cf13c3f5bfb7..6896a2e77d36 100644 --- a/weblate/trans/models/translation.py +++ b/weblate/trans/models/translation.py @@ -271,7 +271,7 @@ def get_filename(self) -> None | str: def load_store(self, fileobj=None, force_intermediate=False): """Load translate-toolkit storage from disk.""" # Use intermediate store as template for source translation - with sentry_sdk.start_span(op="load_store", description=self.get_filename()): + with sentry_sdk.start_span(op="load_store", name=self.get_filename()): if force_intermediate or (self.is_template and self.component.intermediate): template = self.component.intermediate_store else: @@ -329,7 +329,7 @@ def sync_unit( is_new = True with sentry_sdk.start_span( - op="update_from_unit", description=f"{self.full_slug}:{pos}" + op="update_from_unit", name=f"{self.full_slug}:{pos}" ): newunit.update_from_unit(unit, pos, is_new) @@ -338,7 +338,7 @@ def sync_unit( def check_sync(self, force=False, request=None, change=None) -> None: # noqa: C901 """Check whether database is in sync with git and possibly updates.""" - with sentry_sdk.start_span(op="check_sync", description=self.full_slug): + with sentry_sdk.start_span(op="check_sync", name=self.full_slug): if change is None: change = Change.ACTION_UPDATE user = None if request is None else request.user diff --git a/weblate/trans/views/edit.py b/weblate/trans/views/edit.py index 34dad864a207..4f492aacd868 100644 --- a/weblate/trans/views/edit.py +++ b/weblate/trans/views/edit.py @@ -83,7 +83,7 @@ def display_fixups(request: AuthenticatedHttpRequest, fixups) -> None: def get_other_units(unit): """Return other units to show while translating.""" - with sentry_sdk.start_span(op="unit.others", description=unit.pk): + with sentry_sdk.start_span(op="unit.others", name=unit.pk): result: dict[str, Any] = { "total": 0, "skipped": False, @@ -240,7 +240,7 @@ def search( name = "" search_items = () - with sentry_sdk.start_span(op="unit.search", description=search_url): + with sentry_sdk.start_span(op="unit.search", name=search_url): search_result = { "form": form, "offset": cleaned_data.get("offset", 1), diff --git a/weblate/utils/lock.py b/weblate/utils/lock.py index 05aa5ed87fb4..b2614bec44f1 100644 --- a/weblate/utils/lock.py +++ b/weblate/utils/lock.py @@ -84,7 +84,7 @@ def __enter__(self): self._depth += 1 if self._depth > 1: return - with sentry_sdk.start_span(op="lock.wait", description=self._name): + with sentry_sdk.start_span(op="lock.wait", name=self._name): self._enter_implementation() def __exit__(self, exc_type, exc_value, traceback): diff --git a/weblate/utils/stats.py b/weblate/utils/stats.py index 815efe33a52e..a050a6e16895 100644 --- a/weblate/utils/stats.py +++ b/weblate/utils/stats.py @@ -363,9 +363,7 @@ def update_stats(self, update_parents: bool = True) -> None: self.save(update_parents=update_parents) def calculate_basic(self) -> None: - with sentry_sdk.start_span( - op="stats", description=f"CALCULATE {self.cache_key}" - ): + with sentry_sdk.start_span(op="stats", name=f"CALCULATE {self.cache_key}"): self.ensure_loaded() self._calculate_basic() diff --git a/weblate/vcs/git.py b/weblate/vcs/git.py index ef74d2ec01fd..c9e34713e777 100644 --- a/weblate/vcs/git.py +++ b/weblate/vcs/git.py @@ -1158,7 +1158,7 @@ def request( next_api_time = cache.get(cache_id) now = time() if next_api_time is not None and now < next_api_time: - with sentry_sdk.start_span(op="api_sleep", description=vcs_id): + with sentry_sdk.start_span(op="api_sleep", name=vcs_id): sleep(next_api_time - now) try: response = requests.request( From 404a3c139b194b4ed25e2c1d3d2b621ab6605356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Wed, 2 Oct 2024 11:40:48 +0200 Subject: [PATCH 55/61] feat(search): lazily import dateparser It can be expensive to import due to building thousands of regexps at import time (see https://github.com/scrapinghub/dateparser/pull/1181). --- weblate/utils/search.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/weblate/utils/search.py b/weblate/utils/search.py index 55ace21e34bb..6cf665a6d8d2 100644 --- a/weblate/utils/search.py +++ b/weblate/utils/search.py @@ -12,7 +12,6 @@ from operator import and_, or_ from typing import Any, cast, overload -from dateparser import parse as dateparser_parse from dateutil.parser import ParserError from dateutil.parser import parse as dateutil_parse from django.db import transaction @@ -240,6 +239,21 @@ def convert_datetime(self, text, hour=5, minute=55, second=55, microsecond=0): ), ) + return self.human_date_parse(text, hour, minute, second, microsecond) + + def human_date_parse( + self, + text: str, + hour: int = 5, + minute: int = 55, + second: int = 55, + microsecond: int = 0, + ) -> datetime | tuple[datetime, datetime]: + # Lazily import as this can be expensive + from dateparser import parse as dateparser_parse + + tzinfo = timezone.get_current_timezone() + # Attempts to parse the text using dateparser # If the text is unparsable it will return None result = dateparser_parse(text) From ee1bb4a7f9dafbe5a431f72eaf3b3921527c503a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Wed, 2 Oct 2024 11:49:14 +0200 Subject: [PATCH 56/61] chore: lazily import some external dependencies These contribute considerably to startup time and are not always needed. --- weblate/formats/convert.py | 8 ++++++-- weblate/memory/models.py | 6 ++++-- weblate/utils/request.py | 5 +++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/weblate/formats/convert.py b/weblate/formats/convert.py index 3a92214c370a..469a427d99ab 100644 --- a/weblate/formats/convert.py +++ b/weblate/formats/convert.py @@ -18,14 +18,12 @@ from django.utils.translation import gettext_lazy from translate.convert.po2html import po2html from translate.convert.po2idml import translate_idml, write_idml -from translate.convert.po2md import MarkdownTranslator from translate.convert.po2rc import rerc from translate.convert.po2txt import po2txt from translate.convert.rc2po import rc2po from translate.convert.xliff2odf import translate_odf, write_odf from translate.storage.html import htmlfile from translate.storage.idml import INLINE_ELEMENTS, NO_TRANSLATE_ELEMENTS, open_idml -from translate.storage.markdown import MarkdownFile from translate.storage.odf_io import open_odf from translate.storage.odf_shared import inline_elements, no_translate_content_elements from translate.storage.po import pofile @@ -315,12 +313,18 @@ class MarkdownFormat(ConvertFormat): check_flags = ("safe-html", "strict-same", "md-text") def convertfile(self, storefile, template_store): + # Lazy import as mistletoe is expensive + from translate.storage.markdown import MarkdownFile + # Fake input file with a blank filename mdparser = MarkdownFile(inputfile=NamedBytesIO("", storefile.read())) return self.convert_to_po(mdparser, template_store, use_location=False) def save_content(self, handle) -> None: """Store content to file.""" + # Lazy import as mistletoe is expensive + from translate.convert.po2md import MarkdownTranslator + converter = MarkdownTranslator( inputstore=self.store, includefuzzy=True, outputthreshold=None, maxlength=80 ) diff --git a/weblate/memory/models.py b/weblate/memory/models.py index a48ed7ec69ad..e10e610e2c1d 100644 --- a/weblate/memory/models.py +++ b/weblate/memory/models.py @@ -15,8 +15,6 @@ from django.db.models.functions import MD5 from django.utils.encoding import force_str from django.utils.translation import gettext, pgettext -from jsonschema import validate -from jsonschema.exceptions import ValidationError from translate.misc.xml_helpers import getXMLlang, getXMLspace from translate.storage.tmx import tmxfile from weblate_schemas import load_schema @@ -170,6 +168,10 @@ def import_file( def import_json( self, request: AuthenticatedHttpRequest, fileobj, origin=None, **kwargs ): + # Lazily import as this is expensive + from jsonschema import validate + from jsonschema.exceptions import ValidationError + content = fileobj.read() try: data = json.loads(force_str(content)) diff --git a/weblate/utils/request.py b/weblate/utils/request.py index 3367a839fb04..ccfe03490180 100644 --- a/weblate/utils/request.py +++ b/weblate/utils/request.py @@ -5,8 +5,6 @@ from typing import TYPE_CHECKING -import user_agents - if TYPE_CHECKING: from weblate.auth.models import AuthenticatedHttpRequest @@ -30,6 +28,9 @@ def get_user_agent_raw(request: AuthenticatedHttpRequest) -> str: def get_user_agent(request: AuthenticatedHttpRequest, max_length: int = 200) -> str: """Return formatted user agent for request.""" + # Lazily import as this is expensive + import user_agents + raw = get_user_agent_raw(request) if not raw: return "" From 061db6b3f1aa0c8067ee9f1fafe4393165906cbe Mon Sep 17 00:00:00 2001 From: Mehdi Eloualy <92476321+meel-hd@users.noreply.github.com> Date: Wed, 2 Oct 2024 13:49:46 +0100 Subject: [PATCH 57/61] docs Add missing word in testing docs (#12657) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add missing word in testing docs * Clarify sourcing test-database.sh Co-authored-by: Michal Čihař --- docs/contributing/tests.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contributing/tests.rst b/docs/contributing/tests.rst index 1c7ff9d7dc75..d48c6144d39d 100644 --- a/docs/contributing/tests.rst +++ b/docs/contributing/tests.rst @@ -26,7 +26,7 @@ There are several jobs to verify different aspects: The configuration for the CI is in :file:`.github/workflows` directory. It heavily uses helper scripts stored in :file:`ci` directory. The scripts can be also executed manually, but they require several environment variables, mostly -defining Django settings file to use and database connection. The example +defining Django settings file to use and test database connection. The example definition of that is in :file:`scripts/test-database.sh`: .. literalinclude:: ../../scripts/test-database.sh @@ -36,7 +36,7 @@ The simple execution can look like: .. code-block:: sh - . scripts/test-database.sh + source scripts/test-database.sh ./ci/run-migrate ./ci/run-test ./ci/run-docs From b3ac62d6026b1e8225f67d1efaa66ca9360d8510 Mon Sep 17 00:00:00 2001 From: Mehdi Eloualy <92476321+meel-hd@users.noreply.github.com> Date: Wed, 2 Oct 2024 15:10:49 +0100 Subject: [PATCH 58/61] feat: add keyboard shortcuts modal and functionality (#12430) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds a new keyboard shortcuts modal and binds it to "?" shortcut. The modal displays a table of available keyboard shortcuts and their corresponding actions. The keyboard shortcuts are implemented using the Mousetrap library. The modal is triggered by pressing the "?" key. * updated the ID of the keyboard shortcuts modal to "shortcutsModal" for better accessibility * Refactor keyboard shortcuts modal for better translation support * Add translation context to shot phrases in keyboard-shortcuts.html * Update keyboard-shortcuts.html * small sentence corrections * docs: document keyboard shortcut changes * feat(keyboard-shortcuts): Add conditional check for input fields This commit adds a conditional check in the keyboard-shortcuts.js file to prevent the shortcuts modal from opening when an input field or textarea is focused. The check ensures that the modal is only shown when the user presses the "?" key outside of input fields or textareas. Fixes #5064 Co-authored-by: Benjamin Alan Jamie Co-authored-by: Michal Čihař --- docs/changes.rst | 1 + docs/user/translating.rst | 30 ++-- weblate/static/js/keyboard-shortcuts.js | 19 +++ weblate/static/styles/main.css | 13 ++ weblate/templates/base.html | 2 + weblate/templates/keyboard-shortcuts.html | 176 ++++++++++++++++++++++ 6 files changed, 227 insertions(+), 14 deletions(-) create mode 100644 weblate/static/js/keyboard-shortcuts.js create mode 100644 weblate/templates/keyboard-shortcuts.html diff --git a/docs/changes.rst b/docs/changes.rst index daea2dde8e43..023a7e6f058a 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -19,6 +19,7 @@ Not yet released. * :ref:`autofix` for Devanagari danda now better handles latin script. * :ref:`autofix` for French and Breton now uses a non-breaking space before colons instead of a narrow one. * :ref:`api` now has a preview OpenAPI specification. +* :kbd:`?` now displays available :ref:`keyboard`. **Bug fixes** diff --git a/docs/user/translating.rst b/docs/user/translating.rst index ab3914b6ddc0..b7a28d4fffd6 100644 --- a/docs/user/translating.rst +++ b/docs/user/translating.rst @@ -181,11 +181,13 @@ The following keyboard shortcuts can be utilized during translation: +-------------------------------------------+-----------------------------------------------------------------------+ | Keyboard shortcut | Description | +===========================================+=======================================================================+ -| :kbd:`Alt+Home` | Navigate to first translation in current search. | +| :kbd:`?` | Open available keyboard shortcuts. | +-------------------------------------------+-----------------------------------------------------------------------+ -| :kbd:`Alt+End` | Navigate to last translation in current search. | +| :kbd:`Alt+Home` | Navigate to the first translation in the current search. | +-------------------------------------------+-----------------------------------------------------------------------+ -| :kbd:`Alt+PageUp` or | Navigate to previous translation in current search. | +| :kbd:`Alt+End` | Navigate to the last translation in the current search. | ++-------------------------------------------+-----------------------------------------------------------------------+ +| :kbd:`Alt+PageUp` or | Navigate to the previous translation in the current search. | | | | | :kbd:`Ctrl+↑` or | | | | | @@ -193,7 +195,7 @@ The following keyboard shortcuts can be utilized during translation: | | | | :kbd:`Cmd+↑` | | +-------------------------------------------+-----------------------------------------------------------------------+ -| :kbd:`Alt+PageDown` or | Navigate to next translation in current search. | +| :kbd:`Alt+PageDown` or | Navigate to the next translation in the current search. | | | | | :kbd:`Ctrl+↓` or | | | | | @@ -205,19 +207,19 @@ The following keyboard shortcuts can be utilized during translation: | | pressing :guilabel:`Save and continue` while editing translation. | | :kbd:`Cmd+Enter` | | +-------------------------------------------+-----------------------------------------------------------------------+ -| :kbd:`Ctrl+Shift+Enter` or | Unmark translation as needing edit and submit it. | +| :kbd:`Ctrl+Shift+Enter` or | Unmark translation as Needing edit and submit it. | | | | | :kbd:`Cmd+Shift+Enter` | | +-------------------------------------------+-----------------------------------------------------------------------+ -| :kbd:`Alt+Enter` or | Submit the string as a suggestion; this is same as | +| :kbd:`Alt+Enter` or | Submit the string as a suggestion; this works the same as | | | pressing :guilabel:`Suggest` while editing translation. | | :kbd:`Option+Enter` | | +-------------------------------------------+-----------------------------------------------------------------------+ -| :kbd:`Ctrl+E` or | Focus translation editor. | +| :kbd:`Ctrl+E` or | Focus on translation editor. | | | | | :kbd:`Cmd+E` | | +-------------------------------------------+-----------------------------------------------------------------------+ -| :kbd:`Ctrl+U` or | Focus comment editor. | +| :kbd:`Ctrl+U` or | Focus on comment editor. | | | | | :kbd:`Cmd+U` | | +-------------------------------------------+-----------------------------------------------------------------------+ @@ -225,7 +227,7 @@ The following keyboard shortcuts can be utilized during translation: | | see :ref:`machine-translation`. | | :kbd:`Cmd+M` | | +-------------------------------------------+-----------------------------------------------------------------------+ -| :kbd:`Ctrl+1` to :kbd:`Ctrl+9` or | Copies placeable of given number from source string. | +| :kbd:`Ctrl+1` to :kbd:`Ctrl+9` or | Copies placeable of a given number from source string. | | | | | :kbd:`Cmd+1` to :kbd:`Cmd+9` | | +-------------------------------------------+-----------------------------------------------------------------------+ @@ -245,11 +247,11 @@ The following keyboard shortcuts can be utilized during translation: | | | | :kbd:`Cmd+J` | | +-------------------------------------------+-----------------------------------------------------------------------+ -| :kbd:`Ctrl+S` or | Focus search field. | +| :kbd:`Ctrl+S` or | Focus on search field. | | | | | :kbd:`Cmd+S` | | +-------------------------------------------+-----------------------------------------------------------------------+ -| :kbd:`Ctrl+O` or | Copy source string. | +| :kbd:`Ctrl+O` or | Copy the source string. | | | | | :kbd:`Cmd+O` | | +-------------------------------------------+-----------------------------------------------------------------------+ @@ -257,9 +259,9 @@ The following keyboard shortcuts can be utilized during translation: | | | | :kbd:`Cmd+Y` | | +-------------------------------------------+-----------------------------------------------------------------------+ -| :kbd:`→` and | Browse the next translation strings. | -| | | -| :kbd:`←` | Browse the previous translation strings. | +| :kbd:`→` | Browse the next translation string. | ++-------------------------------------------+-----------------------------------------------------------------------+ +| :kbd:`←` | Browse the previous translation string. | +-------------------------------------------+-----------------------------------------------------------------------+ .. _visual-keyboard: diff --git a/weblate/static/js/keyboard-shortcuts.js b/weblate/static/js/keyboard-shortcuts.js new file mode 100644 index 000000000000..600bfab5a5c8 --- /dev/null +++ b/weblate/static/js/keyboard-shortcuts.js @@ -0,0 +1,19 @@ +// Copyright © Michal Čihař +// +// SPDX-License-Identifier: GPL-3.0-or-later + +(() => { + Mousetrap.bindGlobal("?", (event) => { + const target = event.target || event.srcElement; + const tagName = target.tagName.toLowerCase(); + if ( + tagName === "input" || + tagName === "textarea" || + target.isContentEditable + ) { + return true; + } + $("#shortcuts-modal").modal("show"); + return false; + }); +})(); diff --git a/weblate/static/styles/main.css b/weblate/static/styles/main.css index 1de5cbcb18ce..ed06523aec2a 100644 --- a/weblate/static/styles/main.css +++ b/weblate/static/styles/main.css @@ -2114,3 +2114,16 @@ tbody.warning { .daterangepicker { border: var(--border) !important; } + +#shortcuts-table-container { + max-height: 60vh; + overflow-y: auto; +} + +#shortcuts-table-container #t-head { + font-weight: bold; +} + +#shortcuts-table-container kbd { + color: unset; +} diff --git a/weblate/templates/base.html b/weblate/templates/base.html index 90b5a60a8e0f..7607bcd798a6 100644 --- a/weblate/templates/base.html +++ b/weblate/templates/base.html @@ -76,6 +76,7 @@ + {% endcompress %} {% block extra_script %} @@ -378,6 +379,7 @@
    +{% include 'keyboard-shortcuts.html' %} {% include 'footer.html' %} diff --git a/weblate/templates/keyboard-shortcuts.html b/weblate/templates/keyboard-shortcuts.html new file mode 100644 index 000000000000..4e1e05f05a90 --- /dev/null +++ b/weblate/templates/keyboard-shortcuts.html @@ -0,0 +1,176 @@ +{% load i18n %} + + + From 124070f815b98bb52d32c64ef7c354009f4bb4ea Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 19:04:14 +0200 Subject: [PATCH 59/61] chore(deps): update dependency @sentry/browser to v8.33.0 (#12660) * chore(deps): update dependency @sentry/browser to v8.33.0 * chore(js): update vendored libraries --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] --- client/package.json | 2 +- client/yarn.lock | 140 ++++++++++++++--------------- weblate/static/js/vendor/sentry.js | 2 +- 3 files changed, 72 insertions(+), 72 deletions(-) diff --git a/client/package.json b/client/package.json index c97a48a4a277..f5f38a4f3ebb 100644 --- a/client/package.json +++ b/client/package.json @@ -7,7 +7,7 @@ "build": "webpack" }, "dependencies": { - "@sentry/browser": "8.32.0", + "@sentry/browser": "8.33.0", "jquery": "3.7.1" }, "devDependencies": { diff --git a/client/yarn.lock b/client/yarn.lock index 0334b058fe08..b9239912bdaf 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -47,76 +47,76 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@sentry-internal/browser-utils@8.32.0": - version "8.32.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.32.0.tgz#9bcea7d107d5adc82ed9331168468ee44eef8f2d" - integrity sha512-DpUGhk5O1OVjT0fo9wsbEdO1R/S9gGBRDtn9+FFVeRtieJHwXpeZiLK+tZhTOvaILmtSoTPUEY3L5sK4j5Xq9g== - dependencies: - "@sentry/core" "8.32.0" - "@sentry/types" "8.32.0" - "@sentry/utils" "8.32.0" - -"@sentry-internal/feedback@8.32.0": - version "8.32.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.32.0.tgz#ac9a08b060d1016704e42f8c2726ead419f64061" - integrity sha512-XB7hiVJQW1tNzpoXIHbvm3rjipIt7PZiJJtFg2vxaqu/FzdgOcYqQiwIKivJVAKuRZ9rIeJtK1jdXQFOc/TRJA== - dependencies: - "@sentry/core" "8.32.0" - "@sentry/types" "8.32.0" - "@sentry/utils" "8.32.0" - -"@sentry-internal/replay-canvas@8.32.0": - version "8.32.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.32.0.tgz#5d56161b0b62b22cbd49db120e3ea56b370ead8c" - integrity sha512-oBbhtDBkD+5z/T0NVJ5VenBWAid/S9QdVrod/UqxVqU7F8N+E9/INFQI48zCWr4iVlUMcszJPDElvJEsMDvvBQ== - dependencies: - "@sentry-internal/replay" "8.32.0" - "@sentry/core" "8.32.0" - "@sentry/types" "8.32.0" - "@sentry/utils" "8.32.0" - -"@sentry-internal/replay@8.32.0": - version "8.32.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.32.0.tgz#59e3ec7b51c9214eeae9fa617490b89ce6737e1c" - integrity sha512-yiEUnn2yyo1AIQIFNeRX3tdK8fmyKIkxdFS1WiVQmeYI/hFwYBTZPly0FcO/g3xnRMSA2tvrS+hZEaaXfK4WhA== - dependencies: - "@sentry-internal/browser-utils" "8.32.0" - "@sentry/core" "8.32.0" - "@sentry/types" "8.32.0" - "@sentry/utils" "8.32.0" - -"@sentry/browser@8.32.0": - version "8.32.0" - resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.32.0.tgz#3944bc5178e6cfffc8c71ba05920fee7dec5bd38" - integrity sha512-AEKFj64g4iYwEMRvVcxiY0FswmClRXCP1IEvCqujn8OBS8AjMOr1z/RwYieEs0D90yNNB3YEqF8adrKENblJmw== - dependencies: - "@sentry-internal/browser-utils" "8.32.0" - "@sentry-internal/feedback" "8.32.0" - "@sentry-internal/replay" "8.32.0" - "@sentry-internal/replay-canvas" "8.32.0" - "@sentry/core" "8.32.0" - "@sentry/types" "8.32.0" - "@sentry/utils" "8.32.0" - -"@sentry/core@8.32.0": - version "8.32.0" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.32.0.tgz#7c4b74afa7a15bd31f5e6881aac82ccfd753e1d6" - integrity sha512-+xidTr0lZ0c755tq4k75dXPEb8PA+qvIefW3U9+dQMORLokBrYoKYMf5zZTG2k/OfSJS6OSxatUj36NFuCs3aA== - dependencies: - "@sentry/types" "8.32.0" - "@sentry/utils" "8.32.0" - -"@sentry/types@8.32.0": - version "8.32.0" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.32.0.tgz#dfd8aa9449a5f793b9c720888819a74a11f1790d" - integrity sha512-hxckvN2MzS5SgGDgVQ0/QpZXk13Vrq4BtZLwXhPhyeTmZtUiUfWvcL5TFQqLinfKdTKPe9q2MxeAJ0D4LalhMg== - -"@sentry/utils@8.32.0": - version "8.32.0" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.32.0.tgz#99a4298ee8fd7208ade470931c19d71c571dfce8" - integrity sha512-t1WVERhgmYURxbBj9J4/H2P2X+VKqm7B3ce9iQyrZbdf5NekhcU4jHIecPUWCPHjQkFIqkVTorqeBmDTlg/UmQ== - dependencies: - "@sentry/types" "8.32.0" +"@sentry-internal/browser-utils@8.33.0": + version "8.33.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.33.0.tgz#17921cd2b83c835f6b986877d65b2aeb68e4b9b0" + integrity sha512-zwjmD+XI3pgxxiqKGLXYDGSd+zfO7az9zzbLn1le8Vv9cRL2lZyMLcwiwEaTpwz3B0pPONeDZMT8+bzMGRs8zw== + dependencies: + "@sentry/core" "8.33.0" + "@sentry/types" "8.33.0" + "@sentry/utils" "8.33.0" + +"@sentry-internal/feedback@8.33.0": + version "8.33.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.33.0.tgz#dac09d62e7852143ff8cc3081e298828c18ecff7" + integrity sha512-KSW/aiNgmJc8PDl2NsM+ONvGure4tPaluj7O1Nw+947Dh8W6CJnQ9srB7xPyoYYWyQW8Hyl1vzxY9W0J+fjlhA== + dependencies: + "@sentry/core" "8.33.0" + "@sentry/types" "8.33.0" + "@sentry/utils" "8.33.0" + +"@sentry-internal/replay-canvas@8.33.0": + version "8.33.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.33.0.tgz#d498ef94163fca9f79a7f35093ac746d44965b36" + integrity sha512-9fEhMP+gQYQrtn/SQd1Vd7U7emTSGBpLKc5h5f0iV0yDmjYAhNVbq4RgPTYAgnBEcdVo3qgboL6UIz9Dv+dYRQ== + dependencies: + "@sentry-internal/replay" "8.33.0" + "@sentry/core" "8.33.0" + "@sentry/types" "8.33.0" + "@sentry/utils" "8.33.0" + +"@sentry-internal/replay@8.33.0": + version "8.33.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.33.0.tgz#5a1297e05666aee059e2de9f278889ab5c405f44" + integrity sha512-GFBaDA4yhlEf3wTXOVXnJVG/diuKxeqZuXcuhsAwJb+YcFR0NhgsRn3wIGuYOZZF8GBXzx9PFnb9yIuFgx5Nbw== + dependencies: + "@sentry-internal/browser-utils" "8.33.0" + "@sentry/core" "8.33.0" + "@sentry/types" "8.33.0" + "@sentry/utils" "8.33.0" + +"@sentry/browser@8.33.0": + version "8.33.0" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.33.0.tgz#077fce0014d0674405920c87c8aed03be11d4815" + integrity sha512-qu/g20ZskywEU8BWc4Fts1kXFFBtw1vS+XvPq7Ta9zCeRG5dlXhhYDVQ4/v4nAL/cs0o6aLCq73m109CFF0Kig== + dependencies: + "@sentry-internal/browser-utils" "8.33.0" + "@sentry-internal/feedback" "8.33.0" + "@sentry-internal/replay" "8.33.0" + "@sentry-internal/replay-canvas" "8.33.0" + "@sentry/core" "8.33.0" + "@sentry/types" "8.33.0" + "@sentry/utils" "8.33.0" + +"@sentry/core@8.33.0": + version "8.33.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.33.0.tgz#407b70c19038b3201a742b3f041ab44fbb7f7397" + integrity sha512-618PQGHQLBVCpAq1s+e/rpIUaLUnj19IPUgn97rUGXLLna8ETIAoyQoG70wz4q9niw4Z4GlS5kZNrael2O3+2w== + dependencies: + "@sentry/types" "8.33.0" + "@sentry/utils" "8.33.0" + +"@sentry/types@8.33.0": + version "8.33.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.33.0.tgz#2613acefae23c53e660c410120d5d4cbcfc5d713" + integrity sha512-V/A+72ZdnfGtXeXIpz1kUo3LRdq3WKEYYFUR2RKpCdPh9yeOrHq6u/rmzTWx49+om0yhZN+JhVoxDzt75UoFRg== + +"@sentry/utils@8.33.0": + version "8.33.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.33.0.tgz#60b7d441e93500f1e547e819e62987d0e544e644" + integrity sha512-TdwtGdevJij2wq2x/hDUr+x5TXt47ZhWxZ8zluai/lnIDTUB3Xs/L9yHtj1J+H9hr8obkMASE9IanUrWXzrP6Q== + dependencies: + "@sentry/types" "8.33.0" "@types/estree@^1.0.5": version "1.0.5" diff --git a/weblate/static/js/vendor/sentry.js b/weblate/static/js/vendor/sentry.js index 20a0b2f1f635..83c3bc3222d3 100644 --- a/weblate/static/js/vendor/sentry.js +++ b/weblate/static/js/vendor/sentry.js @@ -1 +1 @@ -(()=>{"use strict";const t="undefined"==typeof __SENTRY_DEBUG__||__SENTRY_DEBUG__,e="8.32.0",n=globalThis;function r(t,r,o){const s=o||n,i=s.__SENTRY__=s.__SENTRY__||{},a=i[e]=i[e]||{};return a[t]||(a[t]=r())}const o=["debug","info","warn","error","log","assert","trace"],s={};function i(t){if(!("console"in n))return t();const e=n.console,r={},o=Object.keys(s);o.forEach((t=>{const n=s[t];r[t]=e[t],e[t]=n}));try{return t()}finally{o.forEach((t=>{e[t]=r[t]}))}}const a=r("logger",(function(){let e=!1;const r={enable:()=>{e=!0},disable:()=>{e=!1},isEnabled:()=>e};return t?o.forEach((t=>{r[t]=(...r)=>{e&&i((()=>{n.console[t](`Sentry Logger [${t}]:`,...r)}))}})):o.forEach((t=>{r[t]=()=>{}})),r})),c=Object.prototype.toString;function u(t){switch(c.call(t)){case"[object Error]":case"[object Exception]":case"[object DOMException]":return!0;default:return v(t,Error)}}function l(t,e){return c.call(t)===`[object ${e}]`}function p(t){return l(t,"ErrorEvent")}function d(t){return l(t,"DOMError")}function f(t){return l(t,"String")}function h(t){return"object"==typeof t&&null!==t&&"__sentry_template_string__"in t&&"__sentry_template_values__"in t}function m(t){return null===t||h(t)||"object"!=typeof t&&"function"!=typeof t}function _(t){return l(t,"Object")}function g(t){return"undefined"!=typeof Event&&v(t,Event)}function y(t){return Boolean(t&&t.then&&"function"==typeof t.then)}function v(t,e){try{return t instanceof e}catch(t){return!1}}function E(t){return!("object"!=typeof t||null===t||!t.__isVue&&!t._isVue)}const b=n,S=80;function x(t,e={}){if(!t)return"";try{let n=t;const r=5,o=[];let s=0,i=0;const a=" > ",c=a.length;let u;const l=Array.isArray(e)?e:e.keyAttrs,p=!Array.isArray(e)&&e.maxStringLength||S;for(;n&&s++1&&i+o.length*c+u.length>=p));)o.push(u),i+=u.length,n=n.parentNode;return o.reverse().join(a)}catch(t){return""}}function w(t,e){const n=t,r=[];if(!n||!n.tagName)return"";if(b.HTMLElement&&n instanceof HTMLElement&&n.dataset){if(n.dataset.sentryComponent)return n.dataset.sentryComponent;if(n.dataset.sentryElement)return n.dataset.sentryElement}r.push(n.tagName.toLowerCase());const o=e&&e.length?e.filter((t=>n.getAttribute(t))).map((t=>[t,n.getAttribute(t)])):null;if(o&&o.length)o.forEach((t=>{r.push(`[${t[0]}="${t[1]}"]`)}));else{n.id&&r.push(`#${n.id}`);const t=n.className;if(t&&f(t)){const e=t.split(/\s+/);for(const t of e)r.push(`.${t}`)}}const s=["aria-label","type","name","title","alt"];for(const t of s){const e=n.getAttribute(t);e&&r.push(`[${t}="${e}"]`)}return r.join("")}function k(t,e=0){return"string"!=typeof t||0===e||t.length<=e?t:`${t.slice(0,e)}...`}function $(t,e){if(!Array.isArray(t))return"";const n=[];for(let e=0;eO(t,e,n)))}function D(t,e,n){if(!(e in t))return;const r=t[e],o=n(r);"function"==typeof o&&I(o,r),t[e]=o}function N(e,n,r){try{Object.defineProperty(e,n,{value:r,writable:!0,configurable:!0})}catch(r){t&&a.log(`Failed to add non-enumerable property "${n}" to object`,e)}}function I(t,e){try{const n=e.prototype||{};t.prototype=e.prototype=n,N(t,"__sentry_original__",e)}catch(t){}}function P(t){return t.__sentry_original__}function R(t){if(u(t))return{message:t.message,name:t.name,stack:t.stack,...j(t)};if(g(t)){const e={type:t.type,target:C(t.target),currentTarget:C(t.currentTarget),...j(t)};return"undefined"!=typeof CustomEvent&&v(t,CustomEvent)&&(e.detail=t.detail),e}return t}function C(t){try{return e=t,"undefined"!=typeof Element&&v(e,Element)?x(t):Object.prototype.toString.call(t)}catch(t){return""}var e}function j(t){if("object"==typeof t&&null!==t){const e={};for(const n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e}return{}}function A(t){return L(t,new Map)}function L(t,e){if(function(t){if(!_(t))return!1;try{const e=Object.getPrototypeOf(t).constructor.name;return!e||"Object"===e}catch(t){return!0}}(t)){const n=e.get(t);if(void 0!==n)return n;const r={};e.set(t,r);for(const n of Object.keys(t))void 0!==t[n]&&(r[n]=L(t[n],e));return r}if(Array.isArray(t)){const n=e.get(t);if(void 0!==n)return n;const r=[];return e.set(t,r),t.forEach((t=>{r.push(L(t,e))})),r}return t}function M(){const t=n,e=t.crypto||t.msCrypto;let r=()=>16*Math.random();try{if(e&&e.randomUUID)return e.randomUUID().replace(/-/g,"");e&&e.getRandomValues&&(r=()=>{const t=new Uint8Array(1);return e.getRandomValues(t),t[0]})}catch(t){}return([1e7]+1e3+4e3+8e3+1e11).replace(/[018]/g,(t=>(t^(15&r())>>t/4).toString(16)))}function U(t){return t.exception&&t.exception.values?t.exception.values[0]:void 0}function q(t){const{message:e,event_id:n}=t;if(e)return e;const r=U(t);return r?r.type&&r.value?`${r.type}: ${r.value}`:r.type||r.value||n||"":n||""}function F(t,e,n){const r=t.exception=t.exception||{},o=r.values=r.values||[],s=o[0]=o[0]||{};s.value||(s.value=e||""),s.type||(s.type=n||"Error")}function Y(t,e){const n=U(t);if(!n)return;const r=n.mechanism;if(n.mechanism={type:"generic",handled:!0,...r,...e},e&&"data"in e){const t={...r&&r.data,...e.data};n.mechanism.data=t}}function B(t){if(t&&t.__sentry_captured__)return!0;try{N(t,"__sentry_captured__",!0)}catch(t){}return!1}function H(t){return Array.isArray(t)?t:[t]}const G="undefined"==typeof __SENTRY_DEBUG__||__SENTRY_DEBUG__,W=[];function z(t){const e=t.defaultIntegrations||[],n=t.integrations;let r;e.forEach((t=>{t.isDefaultInstance=!0})),r=Array.isArray(n)?[...e,...n]:"function"==typeof n?H(n(e)):e;const o=function(t){const e={};return t.forEach((t=>{const{name:n}=t,r=e[n];r&&!r.isDefaultInstance&&t.isDefaultInstance||(e[n]=t)})),Object.values(e)}(r),s=o.findIndex((t=>"Debug"===t.name));if(s>-1){const[t]=o.splice(s,1);o.push(t)}return o}function J(t,e){for(const n of e)n&&n.afterAllSetup&&n.afterAllSetup(t)}function V(t,e,n){if(n[e.name])G&&a.log(`Integration skipped because it was already installed: ${e.name}`);else{if(n[e.name]=e,-1===W.indexOf(e.name)&&"function"==typeof e.setupOnce&&(e.setupOnce(),W.push(e.name)),e.setup&&"function"==typeof e.setup&&e.setup(t),"function"==typeof e.preprocessEvent){const n=e.preprocessEvent.bind(e);t.on("preprocessEvent",((e,r)=>n(e,r,t)))}if("function"==typeof e.processEvent){const n=e.processEvent.bind(e),r=Object.assign(((e,r)=>n(e,r,t)),{id:e.name});t.addEventProcessor(r)}G&&a.log(`Integration installed: ${e.name}`)}}const K=[/^Script error\.?$/,/^Javascript error: Script error\.? on line 0$/,/^ResizeObserver loop completed with undelivered notifications.$/,/^Cannot redefine property: googletag$/,"undefined is not an object (evaluating 'a.L')",'can\'t redefine non-configurable property "solana"',"vv().getRestrictions is not a function. (In 'vv().getRestrictions(1,a)', 'vv().getRestrictions' is undefined)","Can't find variable: _AutofillCallbackHandler"],X=(t={})=>({name:"InboundFilters",processEvent(e,n,r){const o=r.getOptions(),s=function(t={},e={}){return{allowUrls:[...t.allowUrls||[],...e.allowUrls||[]],denyUrls:[...t.denyUrls||[],...e.denyUrls||[]],ignoreErrors:[...t.ignoreErrors||[],...e.ignoreErrors||[],...t.disableErrorDefaults?[]:K],ignoreTransactions:[...t.ignoreTransactions||[],...e.ignoreTransactions||[]],ignoreInternal:void 0===t.ignoreInternal||t.ignoreInternal}}(t,o);return function(t,e){if(e.ignoreInternal&&function(t){try{return"SentryError"===t.exception.values[0].type}catch(t){}return!1}(t))return G&&a.warn(`Event dropped due to being internal Sentry Error.\nEvent: ${q(t)}`),!0;if(function(t,e){if(t.type||!e||!e.length)return!1;return function(t){const e=[];t.message&&e.push(t.message);let n;try{n=t.exception.values[t.exception.values.length-1]}catch(t){}n&&n.value&&(e.push(n.value),n.type&&e.push(`${n.type}: ${n.value}`));return e}(t).some((t=>T(t,e)))}(t,e.ignoreErrors))return G&&a.warn(`Event dropped due to being matched by \`ignoreErrors\` option.\nEvent: ${q(t)}`),!0;if(function(t){if(t.type)return!1;if(!t.exception||!t.exception.values||0===t.exception.values.length)return!1;return!t.message&&!t.exception.values.some((t=>t.stacktrace||t.type&&"Error"!==t.type||t.value))}(t))return G&&a.warn(`Event dropped due to not having an error message, error type or stacktrace.\nEvent: ${q(t)}`),!0;if(function(t,e){if("transaction"!==t.type||!e||!e.length)return!1;const n=t.transaction;return!!n&&T(n,e)}(t,e.ignoreTransactions))return G&&a.warn(`Event dropped due to being matched by \`ignoreTransactions\` option.\nEvent: ${q(t)}`),!0;if(function(t,e){if(!e||!e.length)return!1;const n=Q(t);return!!n&&T(n,e)}(t,e.denyUrls))return G&&a.warn(`Event dropped due to being matched by \`denyUrls\` option.\nEvent: ${q(t)}.\nUrl: ${Q(t)}`),!0;if(!function(t,e){if(!e||!e.length)return!0;const n=Q(t);return!n||T(n,e)}(t,e.allowUrls))return G&&a.warn(`Event dropped due to not being matched by \`allowUrls\` option.\nEvent: ${q(t)}.\nUrl: ${Q(t)}`),!0;return!1}(e,s)?null:e}});function Q(t){try{let e;try{e=t.exception.values[0].stacktrace.frames}catch(t){}return e?function(t=[]){for(let e=t.length-1;e>=0;e--){const n=t[e];if(n&&""!==n.filename&&"[native code]"!==n.filename)return n.filename||null}return null}(e):null}catch(e){return G&&a.error(`Cannot extract url for event ${q(t)}`),null}}function Z(){return tt(n),n}function tt(t){const n=t.__SENTRY__=t.__SENTRY__||{};return n.version=n.version||e,n[e]=n[e]||{}}function et(){return{traceId:M(),spanId:M().substring(16)}}const nt=1e3;function rt(){return Date.now()/nt}const ot=function(){const{performance:t}=n;if(!t||!t.now)return rt;const e=Date.now()-t.now(),r=null==t.timeOrigin?e:t.timeOrigin;return()=>(r+t.now())/nt}();let st;(()=>{const{performance:t}=n;if(!t||!t.now)return void(st="none");const e=36e5,r=t.now(),o=Date.now(),s=t.timeOrigin?Math.abs(t.timeOrigin+r-o):e,i=sfunction(t){return A({sid:`${t.sid}`,init:t.init,started:new Date(1e3*t.started).toISOString(),timestamp:new Date(1e3*t.timestamp).toISOString(),status:t.status,errors:t.errors,did:"number"==typeof t.did||"string"==typeof t.did?`${t.did}`:void 0,duration:t.duration,abnormal_mechanism:t.abnormal_mechanism,attrs:{release:t.release,environment:t.environment,ip_address:t.ipAddress,user_agent:t.userAgent}})}(n)};return t&&at(n,t),n}function at(t,e={}){if(e.user&&(!t.ipAddress&&e.user.ip_address&&(t.ipAddress=e.user.ip_address),t.did||e.did||(t.did=e.user.id||e.user.email||e.user.username)),t.timestamp=e.timestamp||ot(),e.abnormal_mechanism&&(t.abnormal_mechanism=e.abnormal_mechanism),e.ignoreDuration&&(t.ignoreDuration=e.ignoreDuration),e.sid&&(t.sid=32===e.sid.length?e.sid:M()),void 0!==e.init&&(t.init=e.init),!t.did&&e.did&&(t.did=`${e.did}`),"number"==typeof e.started&&(t.started=e.started),t.ignoreDuration)t.duration=void 0;else if("number"==typeof e.duration)t.duration=e.duration;else{const e=t.timestamp-t.started;t.duration=e>=0?e:0}e.release&&(t.release=e.release),e.environment&&(t.environment=e.environment),!t.ipAddress&&e.ipAddress&&(t.ipAddress=e.ipAddress),!t.userAgent&&e.userAgent&&(t.userAgent=e.userAgent),"number"==typeof e.errors&&(t.errors=e.errors),e.status&&(t.status=e.status)}const ct="_sentrySpan";function ut(t,e){e?N(t,ct,e):delete t[ct]}function lt(t){return t[ct]}class pt{constructor(){this._notifyingListeners=!1,this._scopeListeners=[],this._eventProcessors=[],this._breadcrumbs=[],this._attachments=[],this._user={},this._tags={},this._extra={},this._contexts={},this._sdkProcessingMetadata={},this._propagationContext=et()}clone(){const t=new pt;return t._breadcrumbs=[...this._breadcrumbs],t._tags={...this._tags},t._extra={...this._extra},t._contexts={...this._contexts},t._user=this._user,t._level=this._level,t._session=this._session,t._transactionName=this._transactionName,t._fingerprint=this._fingerprint,t._eventProcessors=[...this._eventProcessors],t._requestSession=this._requestSession,t._attachments=[...this._attachments],t._sdkProcessingMetadata={...this._sdkProcessingMetadata},t._propagationContext={...this._propagationContext},t._client=this._client,t._lastEventId=this._lastEventId,ut(t,lt(this)),t}setClient(t){this._client=t}setLastEventId(t){this._lastEventId=t}getClient(){return this._client}lastEventId(){return this._lastEventId}addScopeListener(t){this._scopeListeners.push(t)}addEventProcessor(t){return this._eventProcessors.push(t),this}setUser(t){return this._user=t||{email:void 0,id:void 0,ip_address:void 0,username:void 0},this._session&&at(this._session,{user:t}),this._notifyScopeListeners(),this}getUser(){return this._user}getRequestSession(){return this._requestSession}setRequestSession(t){return this._requestSession=t,this}setTags(t){return this._tags={...this._tags,...t},this._notifyScopeListeners(),this}setTag(t,e){return this._tags={...this._tags,[t]:e},this._notifyScopeListeners(),this}setExtras(t){return this._extra={...this._extra,...t},this._notifyScopeListeners(),this}setExtra(t,e){return this._extra={...this._extra,[t]:e},this._notifyScopeListeners(),this}setFingerprint(t){return this._fingerprint=t,this._notifyScopeListeners(),this}setLevel(t){return this._level=t,this._notifyScopeListeners(),this}setTransactionName(t){return this._transactionName=t,this._notifyScopeListeners(),this}setContext(t,e){return null===e?delete this._contexts[t]:this._contexts[t]=e,this._notifyScopeListeners(),this}setSession(t){return t?this._session=t:delete this._session,this._notifyScopeListeners(),this}getSession(){return this._session}update(t){if(!t)return this;const e="function"==typeof t?t(this):t,[n,r]=e instanceof dt?[e.getScopeData(),e.getRequestSession()]:_(e)?[t,t.requestSession]:[],{tags:o,extra:s,user:i,contexts:a,level:c,fingerprint:u=[],propagationContext:l}=n||{};return this._tags={...this._tags,...o},this._extra={...this._extra,...s},this._contexts={...this._contexts,...a},i&&Object.keys(i).length&&(this._user=i),c&&(this._level=c),u.length&&(this._fingerprint=u),l&&(this._propagationContext=l),r&&(this._requestSession=r),this}clear(){return this._breadcrumbs=[],this._tags={},this._extra={},this._user={},this._contexts={},this._level=void 0,this._transactionName=void 0,this._fingerprint=void 0,this._requestSession=void 0,this._session=void 0,ut(this,void 0),this._attachments=[],this._propagationContext=et(),this._notifyScopeListeners(),this}addBreadcrumb(t,e){const n="number"==typeof e?e:100;if(n<=0)return this;const r={timestamp:rt(),...t},o=this._breadcrumbs;return o.push(r),this._breadcrumbs=o.length>n?o.slice(-n):o,this._notifyScopeListeners(),this}getLastBreadcrumb(){return this._breadcrumbs[this._breadcrumbs.length-1]}clearBreadcrumbs(){return this._breadcrumbs=[],this._notifyScopeListeners(),this}addAttachment(t){return this._attachments.push(t),this}clearAttachments(){return this._attachments=[],this}getScopeData(){return{breadcrumbs:this._breadcrumbs,attachments:this._attachments,contexts:this._contexts,tags:this._tags,extra:this._extra,user:this._user,level:this._level,fingerprint:this._fingerprint||[],eventProcessors:this._eventProcessors,propagationContext:this._propagationContext,sdkProcessingMetadata:this._sdkProcessingMetadata,transactionName:this._transactionName,span:lt(this)}}setSDKProcessingMetadata(t){return this._sdkProcessingMetadata={...this._sdkProcessingMetadata,...t},this}setPropagationContext(t){return this._propagationContext=t,this}getPropagationContext(){return this._propagationContext}captureException(t,e){const n=e&&e.event_id?e.event_id:M();if(!this._client)return a.warn("No client configured on scope - will not capture exception!"),n;const r=new Error("Sentry syntheticException");return this._client.captureException(t,{originalException:t,syntheticException:r,...e,event_id:n},this),n}captureMessage(t,e,n){const r=n&&n.event_id?n.event_id:M();if(!this._client)return a.warn("No client configured on scope - will not capture message!"),r;const o=new Error(t);return this._client.captureMessage(t,e,{originalException:t,syntheticException:o,...n,event_id:r},this),r}captureEvent(t,e){const n=e&&e.event_id?e.event_id:M();return this._client?(this._client.captureEvent(t,{...e,event_id:n},this),n):(a.warn("No client configured on scope - will not capture event!"),n)}_notifyScopeListeners(){this._notifyingListeners||(this._notifyingListeners=!0,this._scopeListeners.forEach((t=>{t(this)})),this._notifyingListeners=!1)}}const dt=pt;class ft{constructor(t,e){let n,r;n=t||new dt,r=e||new dt,this._stack=[{scope:n}],this._isolationScope=r}withScope(t){const e=this._pushScope();let n;try{n=t(e)}catch(t){throw this._popScope(),t}return y(n)?n.then((t=>(this._popScope(),t)),(t=>{throw this._popScope(),t})):(this._popScope(),n)}getClient(){return this.getStackTop().client}getScope(){return this.getStackTop().scope}getIsolationScope(){return this._isolationScope}getStackTop(){return this._stack[this._stack.length-1]}_pushScope(){const t=this.getScope().clone();return this._stack.push({client:this.getClient(),scope:t}),t}_popScope(){return!(this._stack.length<=1)&&!!this._stack.pop()}}function ht(){const t=tt(Z());return t.stack=t.stack||new ft(r("defaultCurrentScope",(()=>new dt)),r("defaultIsolationScope",(()=>new dt)))}function mt(t){return ht().withScope(t)}function _t(t,e){const n=ht();return n.withScope((()=>(n.getStackTop().scope=t,e(t))))}function gt(t){return ht().withScope((()=>t(ht().getIsolationScope())))}function yt(t){const e=tt(t);return e.acs?e.acs:{withIsolationScope:gt,withScope:mt,withSetScope:_t,withSetIsolationScope:(t,e)=>gt(e),getCurrentScope:()=>ht().getScope(),getIsolationScope:()=>ht().getIsolationScope()}}function vt(){return yt(Z()).getCurrentScope()}function Et(){return yt(Z()).getIsolationScope()}function bt(){return vt().getClient()}let St;const xt=new WeakMap,wt=()=>({name:"FunctionToString",setupOnce(){St=Function.prototype.toString;try{Function.prototype.toString=function(...t){const e=P(this),n=xt.has(bt())&&void 0!==e?e:this;return St.apply(n,t)}}catch(t){}},setup(t){xt.set(t,!0)}}),kt=50,$t="?",Ot=/\(error: (.*)\)/,Tt=/captureMessage|captureException/;function Dt(...t){const e=t.sort(((t,e)=>t[0]-e[0])).map((t=>t[1]));return(t,n=0,r=0)=>{const o=[],s=t.split("\n");for(let t=n;t1024)continue;const i=Ot.test(n)?n.replace(Ot,"$1"):n;if(!i.match(/\S*Error: /)){for(const t of e){const e=t(i);if(e){o.push(e);break}}if(o.length>=kt+r)break}}return function(t){if(!t.length)return[];const e=Array.from(t);/sentryWrapped/.test(Nt(e).function||"")&&e.pop();e.reverse(),Tt.test(Nt(e).function||"")&&(e.pop(),Tt.test(Nt(e).function||"")&&e.pop());return e.slice(0,kt).map((t=>({...t,filename:t.filename||Nt(e).filename,function:t.function||$t})))}(o.slice(r))}}function Nt(t){return t[t.length-1]||{}}const It="";function Pt(t){try{return t&&"function"==typeof t&&t.name||It}catch(t){return It}}function Rt(t){const e=t.exception;if(e){const t=[];try{return e.values.forEach((e=>{e.stacktrace.frames&&t.push(...e.stacktrace.frames)})),t}catch(t){return}}}const Ct=()=>{let t;return{name:"Dedupe",processEvent(e){if(e.type)return e;try{if(function(t,e){if(!e)return!1;if(function(t,e){const n=t.message,r=e.message;if(!n&&!r)return!1;if(n&&!r||!n&&r)return!1;if(n!==r)return!1;if(!At(t,e))return!1;if(!jt(t,e))return!1;return!0}(t,e))return!0;if(function(t,e){const n=Lt(e),r=Lt(t);if(!n||!r)return!1;if(n.type!==r.type||n.value!==r.value)return!1;if(!At(t,e))return!1;if(!jt(t,e))return!1;return!0}(t,e))return!0;return!1}(e,t))return G&&a.warn("Event dropped due to being a duplicate of previously captured event."),null}catch(t){}return t=e}}};function jt(t,e){let n=Rt(t),r=Rt(e);if(!n&&!r)return!0;if(n&&!r||!n&&r)return!1;if(r.length!==n.length)return!1;for(let t=0;t{console.warn("[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.")})));vt().update(e.initialScope);const n=new t(e);return function(t){vt().setClient(t)}(n),n.init(),n}const Ut="production";function qt(t,e=100,n=1/0){try{return Yt("",t,e,n)}catch(t){return{ERROR:`**non-serializable** (${t})`}}}function Ft(t,e=3,n=102400){const r=qt(t,e);return o=r,function(t){return~-encodeURI(t).split(/%..|./).length}(JSON.stringify(o))>n?Ft(t,e-1,n):r;var o}function Yt(t,e,n=1/0,r=1/0,o=function(){const t="function"==typeof WeakSet,e=t?new WeakSet:[];return[function(n){if(t)return!!e.has(n)||(e.add(n),!1);for(let t=0;t=r){l[t]="[MaxProperties ~]";break}const e=d[t];l[t]=Yt(t,e,c-1,r,o),p++}return i(e),l}var Bt;function Ht(t){return new Wt((e=>{e(t)}))}function Gt(t){return new Wt(((e,n)=>{n(t)}))}!function(t){t[t.PENDING=0]="PENDING";t[t.RESOLVED=1]="RESOLVED";t[t.REJECTED=2]="REJECTED"}(Bt||(Bt={}));class Wt{constructor(t){Wt.prototype.__init.call(this),Wt.prototype.__init2.call(this),Wt.prototype.__init3.call(this),Wt.prototype.__init4.call(this),this._state=Bt.PENDING,this._handlers=[];try{t(this._resolve,this._reject)}catch(t){this._reject(t)}}then(t,e){return new Wt(((n,r)=>{this._handlers.push([!1,e=>{if(t)try{n(t(e))}catch(t){r(t)}else n(e)},t=>{if(e)try{n(e(t))}catch(t){r(t)}else r(t)}]),this._executeHandlers()}))}catch(t){return this.then((t=>t),t)}finally(t){return new Wt(((e,n)=>{let r,o;return this.then((e=>{o=!1,r=e,t&&t()}),(e=>{o=!0,r=e,t&&t()})).then((()=>{o?n(r):e(r)}))}))}__init(){this._resolve=t=>{this._setResult(Bt.RESOLVED,t)}}__init2(){this._reject=t=>{this._setResult(Bt.REJECTED,t)}}__init3(){this._setResult=(t,e)=>{this._state===Bt.PENDING&&(y(e)?e.then(this._resolve,this._reject):(this._state=t,this._value=e,this._executeHandlers()))}}__init4(){this._executeHandlers=()=>{if(this._state===Bt.PENDING)return;const t=this._handlers.slice();this._handlers=[],t.forEach((t=>{t[0]||(this._state===Bt.RESOLVED&&t[1](this._value),this._state===Bt.REJECTED&&t[2](this._value),t[0]=!0)}))}}}function zt(t,e,n,r=0){return new Wt(((o,s)=>{const i=t[r];if(null===e||"function"!=typeof i)o(e);else{const c=i({...e},n);G&&i.id&&null===c&&a.log(`Event processor "${i.id}" dropped event`),y(c)?c.then((e=>zt(t,e,n,r+1).then(o))).then(null,s):zt(t,c,n,r+1).then(o).then(null,s)}}))}const Jt="sentry-",Vt=/^sentry-/;function Kt(t){const e=function(t){if(!t||!f(t)&&!Array.isArray(t))return;if(Array.isArray(t))return t.reduce(((t,e)=>{const n=Xt(e);return Object.entries(n).forEach((([e,n])=>{t[e]=n})),t}),{});return Xt(t)}(t);if(!e)return;const n=Object.entries(e).reduce(((t,[e,n])=>{if(e.match(Vt)){t[e.slice(Jt.length)]=n}return t}),{});return Object.keys(n).length>0?n:void 0}function Xt(t){return t.split(",").map((t=>t.split("=").map((t=>decodeURIComponent(t.trim()))))).reduce(((t,[e,n])=>(e&&n&&(t[e]=n),t)),{})}const Qt="sentry.source",Zt="sentry.sample_rate",te="sentry.op",ee="sentry.origin";const ne="_sentryMetrics";function re(t){const e=t[ne];if(!e)return;const n={};for(const[,[t,r]]of e){(n[t]||(n[t]=[])).push(A(r))}return n}const oe=0,se=1;const ie=1;function ae(t){const{spanId:e,traceId:n}=t.spanContext(),{parent_span_id:r}=le(t);return A({parent_span_id:r,span_id:e,trace_id:n})}function ce(t){return"number"==typeof t?ue(t):Array.isArray(t)?t[0]+t[1]/1e9:t instanceof Date?ue(t.getTime()):ot()}function ue(t){return t>9999999999?t/1e3:t}function le(t){if(function(t){return"function"==typeof t.getSpanJSON}(t))return t.getSpanJSON();try{const{spanId:e,traceId:n}=t.spanContext();if(function(t){const e=t;return!!(e.attributes&&e.startTime&&e.name&&e.endTime&&e.status)}(t)){const{attributes:r,startTime:o,name:s,endTime:i,parentSpanId:a,status:c}=t;return A({span_id:e,trace_id:n,data:r,description:s,parent_span_id:a,start_timestamp:ce(o),timestamp:ce(i)||void 0,status:de(c),op:r[te],origin:r[ee],_metrics_summary:re(t)})}return{span_id:e,trace_id:n}}catch(t){return{}}}function pe(t){const{traceFlags:e}=t.spanContext();return e===ie}function de(t){if(t&&t.code!==oe)return t.code===se?"ok":t.message||"unknown_error"}const fe="_sentryRootSpan";function he(t){return t[fe]||t}const me="_frozenDsc";function _e(t,e){const n=e.getOptions(),{publicKey:r}=e.getDsn()||{},o=A({environment:n.environment||Ut,release:n.release,public_key:r,trace_id:t});return e.emit("createDsc",o),o}function ge(t){const e=bt();if(!e)return{};const n=_e(le(t).trace_id||"",e),r=he(t),o=r[me];if(o)return o;const s=r.spanContext().traceState,i=s&&s.get("sentry.dsc"),a=i&&Kt(i);if(a)return a;const c=le(r),u=c.data||{},l=u[Zt];null!=l&&(n.sample_rate=`${l}`);const p=u[Qt],d=c.description;return"url"!==p&&d&&(n.transaction=d),function(t){if("boolean"==typeof __SENTRY_TRACING__&&!__SENTRY_TRACING__)return!1;const e=bt(),n=t||e&&e.getOptions();return!!n&&(n.enableTracing||"tracesSampleRate"in n||"tracesSampler"in n)}()&&(n.sampled=String(pe(r))),e.emit("createDsc",n,r),n}function ye(t,e){const{fingerprint:n,span:r,breadcrumbs:o,sdkProcessingMetadata:s}=e;!function(t,e){const{extra:n,tags:r,user:o,contexts:s,level:i,transactionName:a}=e,c=A(n);c&&Object.keys(c).length&&(t.extra={...c,...t.extra});const u=A(r);u&&Object.keys(u).length&&(t.tags={...u,...t.tags});const l=A(o);l&&Object.keys(l).length&&(t.user={...l,...t.user});const p=A(s);p&&Object.keys(p).length&&(t.contexts={...p,...t.contexts});i&&(t.level=i);a&&"transaction"!==t.type&&(t.transaction=a)}(t,e),r&&function(t,e){t.contexts={trace:ae(e),...t.contexts},t.sdkProcessingMetadata={dynamicSamplingContext:ge(e),...t.sdkProcessingMetadata};const n=he(e),r=le(n).description;r&&!t.transaction&&"transaction"===t.type&&(t.transaction=r)}(t,r),function(t,e){t.fingerprint=t.fingerprint?H(t.fingerprint):[],e&&(t.fingerprint=t.fingerprint.concat(e));t.fingerprint&&!t.fingerprint.length&&delete t.fingerprint}(t,n),function(t,e){const n=[...t.breadcrumbs||[],...e];t.breadcrumbs=n.length?n:void 0}(t,o),function(t,e){t.sdkProcessingMetadata={...t.sdkProcessingMetadata,...e}}(t,s)}function ve(t,e){const{extra:n,tags:r,user:o,contexts:s,level:i,sdkProcessingMetadata:a,breadcrumbs:c,fingerprint:u,eventProcessors:l,attachments:p,propagationContext:d,transactionName:f,span:h}=e;Ee(t,"extra",n),Ee(t,"tags",r),Ee(t,"user",o),Ee(t,"contexts",s),Ee(t,"sdkProcessingMetadata",a),i&&(t.level=i),f&&(t.transactionName=f),h&&(t.span=h),c.length&&(t.breadcrumbs=[...t.breadcrumbs,...c]),u.length&&(t.fingerprint=[...t.fingerprint,...u]),l.length&&(t.eventProcessors=[...t.eventProcessors,...l]),p.length&&(t.attachments=[...t.attachments,...p]),t.propagationContext={...t.propagationContext,...d}}function Ee(t,e,n){if(n&&Object.keys(n).length){t[e]={...t[e]};for(const r in n)Object.prototype.hasOwnProperty.call(n,r)&&(t[e][r]=n[r])}}function be(t,e,o,s,i,a){const{normalizeDepth:c=3,normalizeMaxBreadth:u=1e3}=t,l={...e,event_id:e.event_id||o.event_id||M(),timestamp:e.timestamp||rt()},p=o.integrations||t.integrations.map((t=>t.name));!function(t,e){const{environment:n,release:r,dist:o,maxValueLength:s=250}=e;"environment"in t||(t.environment="environment"in e?n:Ut);void 0===t.release&&void 0!==r&&(t.release=r);void 0===t.dist&&void 0!==o&&(t.dist=o);t.message&&(t.message=k(t.message,s));const i=t.exception&&t.exception.values&&t.exception.values[0];i&&i.value&&(i.value=k(i.value,s));const a=t.request;a&&a.url&&(a.url=k(a.url,s))}(l,t),function(t,e){e.length>0&&(t.sdk=t.sdk||{},t.sdk.integrations=[...t.sdk.integrations||[],...e])}(l,p),i&&i.emit("applyFrameMetadata",e),void 0===e.type&&function(t,e){const r=n._sentryDebugIds;if(!r)return;let o;const s=Se.get(e);s?o=s:(o=new Map,Se.set(e,o));const i=Object.entries(r).reduce(((t,[n,r])=>{let s;const i=o.get(n);i?s=i:(s=e(n),o.set(n,s));for(let e=s.length-1;e>=0;e--){const n=s[e];if(n.filename){t[n.filename]=r;break}}return t}),{});try{t.exception.values.forEach((t=>{t.stacktrace.frames.forEach((t=>{t.filename&&(t.debug_id=i[t.filename])}))}))}catch(t){}}(l,t.stackParser);const d=function(t,e){if(!e)return t;const n=t?t.clone():new dt;return n.update(e),n}(s,o.captureContext);o.mechanism&&Y(l,o.mechanism);const f=i?i.getEventProcessors():[],h=r("globalScope",(()=>new dt)).getScopeData();if(a){ve(h,a.getScopeData())}if(d){ve(h,d.getScopeData())}const m=[...o.attachments||[],...h.attachments];m.length&&(o.attachments=m),ye(l,h);return zt([...f,...h.eventProcessors],l,o).then((t=>(t&&function(t){const e={};try{t.exception.values.forEach((t=>{t.stacktrace.frames.forEach((t=>{t.debug_id&&(t.abs_path?e[t.abs_path]=t.debug_id:t.filename&&(e[t.filename]=t.debug_id),delete t.debug_id)}))}))}catch(t){}if(0===Object.keys(e).length)return;t.debug_meta=t.debug_meta||{},t.debug_meta.images=t.debug_meta.images||[];const n=t.debug_meta.images;Object.entries(e).forEach((([t,e])=>{n.push({type:"sourcemap",code_file:t,debug_id:e})}))}(t),"number"==typeof c&&c>0?function(t,e,n){if(!t)return null;const r={...t,...t.breadcrumbs&&{breadcrumbs:t.breadcrumbs.map((t=>({...t,...t.data&&{data:qt(t.data,e,n)}})))},...t.user&&{user:qt(t.user,e,n)},...t.contexts&&{contexts:qt(t.contexts,e,n)},...t.extra&&{extra:qt(t.extra,e,n)}};t.contexts&&t.contexts.trace&&r.contexts&&(r.contexts.trace=t.contexts.trace,t.contexts.trace.data&&(r.contexts.trace.data=qt(t.contexts.trace.data,e,n)));t.spans&&(r.spans=t.spans.map((t=>({...t,...t.data&&{data:qt(t.data,e,n)}}))));return r}(t,c,u):t)))}const Se=new WeakMap;function xe(t){if(t)return function(t){return t instanceof dt||"function"==typeof t}(t)||function(t){return Object.keys(t).some((t=>we.includes(t)))}(t)?{captureContext:t}:t}const we=["user","level","extra","contexts","tags","fingerprint","requestSession","propagationContext"];function ke(t,e){return vt().captureEvent(t,e)}function $e(t){const e=bt(),r=Et(),o=vt(),{release:s,environment:i=Ut}=e&&e.getOptions()||{},{userAgent:a}=n.navigator||{},c=it({release:s,environment:i,user:o.getUser()||r.getUser(),...a&&{userAgent:a},...t}),u=r.getSession();return u&&"ok"===u.status&&at(u,{status:"exited"}),Oe(),r.setSession(c),o.setSession(c),c}function Oe(){const t=Et(),e=vt(),n=e.getSession()||t.getSession();n&&function(t,e){let n={};e?n={status:e}:"ok"===t.status&&(n={status:"exited"}),at(t,n)}(n),Te(),t.setSession(),e.setSession()}function Te(){const t=Et(),e=vt(),n=bt(),r=e.getSession()||t.getSession();r&&n&&n.captureSession(r)}function De(t=!1){t?Oe():Te()}const Ne=/^(?:(\w+):)\/\/(?:(\w+)(?::(\w+)?)?@)([\w.-]+)(?::(\d+))?\/(.+)/;function Ie(t,e=!1){const{host:n,path:r,pass:o,port:s,projectId:i,protocol:a,publicKey:c}=t;return`${a}://${c}${e&&o?`:${o}`:""}@${n}${s?`:${s}`:""}/${r?`${r}/`:r}${i}`}function Pe(t){return{protocol:t.protocol,publicKey:t.publicKey||"",pass:t.pass||"",host:t.host,port:t.port||"",path:t.path||"",projectId:t.projectId}}function Re(e){const n="string"==typeof e?function(t){const e=Ne.exec(t);if(!e)return void i((()=>{console.error(`Invalid Sentry Dsn: ${t}`)}));const[n,r,o="",s="",a="",c=""]=e.slice(1);let u="",l=c;const p=l.split("/");if(p.length>1&&(u=p.slice(0,-1).join("/"),l=p.pop()),l){const t=l.match(/^\d+/);t&&(l=t[0])}return Pe({host:s,pass:o,path:u,projectId:l,port:a,protocol:n,publicKey:r})}(e):Pe(e);if(n&&function(e){if(!t)return!0;const{port:n,projectId:r,protocol:o}=e;return!(["protocol","publicKey","host","projectId"].find((t=>!e[t]&&(a.error(`Invalid Sentry Dsn: ${t} missing`),!0)))||(r.match(/^\d+$/)?function(t){return"http"===t||"https"===t}(o)?n&&isNaN(parseInt(n,10))&&(a.error(`Invalid Sentry Dsn: Invalid port ${n}`),1):(a.error(`Invalid Sentry Dsn: Invalid protocol ${o}`),1):(a.error(`Invalid Sentry Dsn: Invalid projectId ${r}`),1)))}(n))return n}const Ce="7";function je(t){const e=t.protocol?`${t.protocol}:`:"",n=t.port?`:${t.port}`:"";return`${e}//${t.host}${n}${t.path?`/${t.path}`:""}/api/`}function Ae(t,e){return n={sentry_key:t.publicKey,sentry_version:Ce,...e&&{sentry_client:`${e.name}/${e.version}`}},Object.keys(n).map((t=>`${encodeURIComponent(t)}=${encodeURIComponent(n[t])}`)).join("&");var n}function Le(t,e,n){return e||`${function(t){return`${je(t)}${t.projectId}/envelope/`}(t)}?${Ae(t,n)}`}const Me=n;function Ue(){if(!("fetch"in Me))return!1;try{return new Headers,new Request("http://www.example.com"),new Response,!0}catch(t){return!1}}function qe(t){return t&&/^function\s+\w+\(\)\s+\{\s+\[native code\]\s+\}$/.test(t.toString())}const Fe={},Ye={};function Be(t,e){Fe[t]=Fe[t]||[],Fe[t].push(e)}function He(t,e){Ye[t]||(e(),Ye[t]=!0)}function Ge(e,n){const r=e&&Fe[e];if(r)for(const o of r)try{o(n)}catch(n){t&&a.error(`Error while triggering instrumentation handler.\nType: ${e}\nName: ${Pt(o)}\nError:`,n)}}const We=n;const ze=n;let Je;function Ve(t){const e="history";Be(e,t),He(e,Ke)}function Ke(){if(!function(){const t=We.chrome,e=t&&t.app&&t.app.runtime,n="history"in We&&!!We.history.pushState&&!!We.history.replaceState;return!e&&n}())return;const t=ze.onpopstate;function e(t){return function(...e){const n=e.length>2?e[2]:void 0;if(n){const t=Je,e=String(n);Je=e;Ge("history",{from:t,to:e})}return t.apply(this,e)}}ze.onpopstate=function(...e){const n=ze.location.href,r=Je;Je=n;if(Ge("history",{from:r,to:n}),t)try{return t.apply(this,e)}catch(t){}},D(ze.history,"pushState",e),D(ze.history,"replaceState",e)}function Xe(t,e=[]){return[t,e]}function Qe(t,e){const[n,r]=t;return[n,[...r,e]]}function Ze(t,e){const n=t[1];for(const t of n){if(e(t,t[0].type))return!0}return!1}function tn(t){return n.__SENTRY__&&n.__SENTRY__.encodePolyfill?n.__SENTRY__.encodePolyfill(t):(new TextEncoder).encode(t)}function en(t){const[e,n]=t;let r=JSON.stringify(e);function o(t){"string"==typeof r?r="string"==typeof t?r+t:[tn(r),t]:r.push("string"==typeof t?tn(t):t)}for(const t of n){const[e,n]=t;if(o(`\n${JSON.stringify(e)}\n`),"string"==typeof n||n instanceof Uint8Array)o(n);else{let t;try{t=JSON.stringify(n)}catch(e){t=JSON.stringify(qt(n))}o(t)}}return"string"==typeof r?r:function(t){const e=t.reduce(((t,e)=>t+e.length),0),n=new Uint8Array(e);let r=0;for(const e of t)n.set(e,r),r+=e.length;return n}(r)}function nn(t){const e="string"==typeof t.data?tn(t.data):t.data;return[A({type:"attachment",length:e.length,filename:t.filename,content_type:t.contentType,attachment_type:t.attachmentType}),e]}const rn={session:"session",sessions:"session",attachment:"attachment",transaction:"transaction",event:"error",client_report:"internal",user_report:"default",profile:"profile",profile_chunk:"profile",replay_event:"replay",replay_recording:"replay",check_in:"monitor",feedback:"feedback",span:"span",statsd:"metric_bucket"};function on(t){return rn[t]}function sn(t){if(!t||!t.sdk)return;const{name:e,version:n}=t.sdk;return{name:e,version:n}}class an extends Error{constructor(t,e="warn"){super(t),this.message=t,this.name=new.target.prototype.constructor.name,Object.setPrototypeOf(this,new.target.prototype),this.logLevel=e}}function cn(t,e,n,r){const o=sn(n),s=t.type&&"replay_event"!==t.type?t.type:"event";!function(t,e){e&&(t.sdk=t.sdk||{},t.sdk.name=t.sdk.name||e.name,t.sdk.version=t.sdk.version||e.version,t.sdk.integrations=[...t.sdk.integrations||[],...e.integrations||[]],t.sdk.packages=[...t.sdk.packages||[],...e.packages||[]])}(t,n&&n.sdk);const i=function(t,e,n,r){const o=t.sdkProcessingMetadata&&t.sdkProcessingMetadata.dynamicSamplingContext;return{event_id:t.event_id,sent_at:(new Date).toISOString(),...e&&{sdk:e},...!!n&&r&&{dsn:Ie(r)},...o&&{trace:A({...o})}}}(t,o,r,e);delete t.sdkProcessingMetadata;return Xe(i,[[{type:s},t]])}const un="Not capturing exception because it's already been captured.";class ln{constructor(t){if(this._options=t,this._integrations={},this._numProcessing=0,this._outcomes={},this._hooks={},this._eventProcessors=[],t.dsn?this._dsn=Re(t.dsn):G&&a.warn("No DSN provided, client will not send events."),this._dsn){const e=Le(this._dsn,t.tunnel,t._metadata?t._metadata.sdk:void 0);this._transport=t.transport({tunnel:this._options.tunnel,recordDroppedEvent:this.recordDroppedEvent.bind(this),...t.transportOptions,url:e})}}captureException(t,e,n){const r=M();if(B(t))return G&&a.log(un),r;const o={event_id:r,...e};return this._process(this.eventFromException(t,o).then((t=>this._captureEvent(t,o,n)))),o.event_id}captureMessage(t,e,n,r){const o={event_id:M(),...n},s=h(t)?t:String(t),i=m(t)?this.eventFromMessage(s,e,o):this.eventFromException(t,o);return this._process(i.then((t=>this._captureEvent(t,o,r)))),o.event_id}captureEvent(t,e,n){const r=M();if(e&&e.originalException&&B(e.originalException))return G&&a.log(un),r;const o={event_id:r,...e},s=(t.sdkProcessingMetadata||{}).capturedSpanScope;return this._process(this._captureEvent(t,o,s||n)),o.event_id}captureSession(t){"string"!=typeof t.release?G&&a.warn("Discarded session because of missing or non-string release"):(this.sendSession(t),at(t,{init:!1}))}getDsn(){return this._dsn}getOptions(){return this._options}getSdkMetadata(){return this._options._metadata}getTransport(){return this._transport}flush(t){const e=this._transport;return e?(this.emit("flush"),this._isClientDoneProcessing(t).then((n=>e.flush(t).then((t=>n&&t))))):Ht(!0)}close(t){return this.flush(t).then((t=>(this.getOptions().enabled=!1,this.emit("close"),t)))}getEventProcessors(){return this._eventProcessors}addEventProcessor(t){this._eventProcessors.push(t)}init(){(this._isEnabled()||this._options.integrations.some((({name:t})=>t.startsWith("Spotlight"))))&&this._setupIntegrations()}getIntegrationByName(t){return this._integrations[t]}addIntegration(t){const e=this._integrations[t.name];V(this,t,this._integrations),e||J(this,[t])}sendEvent(t,e={}){this.emit("beforeSendEvent",t,e);let n=cn(t,this._dsn,this._options._metadata,this._options.tunnel);for(const t of e.attachments||[])n=Qe(n,nn(t));const r=this.sendEnvelope(n);r&&r.then((e=>this.emit("afterSendEvent",t,e)),null)}sendSession(t){const e=function(t,e,n,r){const o=sn(n);return Xe({sent_at:(new Date).toISOString(),...o&&{sdk:o},...!!r&&e&&{dsn:Ie(e)}},["aggregates"in t?[{type:"sessions"},t]:[{type:"session"},t.toJSON()]])}(t,this._dsn,this._options._metadata,this._options.tunnel);this.sendEnvelope(e)}recordDroppedEvent(t,e,n){if(this._options.sendClientReports){const r="number"==typeof n?n:1,o=`${t}:${e}`;G&&a.log(`Recording outcome: "${o}"${r>1?` (${r} times)`:""}`),this._outcomes[o]=(this._outcomes[o]||0)+r}}on(t,e){const n=this._hooks[t]=this._hooks[t]||[];return n.push(e),()=>{const t=n.indexOf(e);t>-1&&n.splice(t,1)}}emit(t,...e){const n=this._hooks[t];n&&n.forEach((t=>t(...e)))}sendEnvelope(t){return this.emit("beforeEnvelope",t),this._isEnabled()&&this._transport?this._transport.send(t).then(null,(t=>(G&&a.error("Error while sending event:",t),t))):(G&&a.error("Transport disabled"),Ht({}))}_setupIntegrations(){const{integrations:t}=this._options;this._integrations=function(t,e){const n={};return e.forEach((e=>{e&&V(t,e,n)})),n}(this,t),J(this,t)}_updateSessionFromEvent(t,e){let n=!1,r=!1;const o=e.exception&&e.exception.values;if(o){r=!0;for(const t of o){const e=t.mechanism;if(e&&!1===e.handled){n=!0;break}}}const s="ok"===t.status;(s&&0===t.errors||s&&n)&&(at(t,{...n&&{status:"crashed"},errors:t.errors||Number(r||n)}),this.captureSession(t))}_isClientDoneProcessing(t){return new Wt((e=>{let n=0;const r=setInterval((()=>{0==this._numProcessing?(clearInterval(r),e(!0)):(n+=1,t&&n>=t&&(clearInterval(r),e(!1)))}),1)}))}_isEnabled(){return!1!==this.getOptions().enabled&&void 0!==this._transport}_prepareEvent(t,e,n,r=Et()){const o=this.getOptions(),s=Object.keys(this._integrations);return!e.integrations&&s.length>0&&(e.integrations=s),this.emit("preprocessEvent",t,e),t.type||r.setLastEventId(t.event_id||e.event_id),be(o,t,e,n,this,r).then((t=>{if(null===t)return t;const e={...r.getPropagationContext(),...n?n.getPropagationContext():void 0};if(!(t.contexts&&t.contexts.trace)&&e){const{traceId:n,spanId:r,parentSpanId:o,dsc:s}=e;t.contexts={trace:A({trace_id:n,span_id:r,parent_span_id:o}),...t.contexts};const i=s||_e(n,this);t.sdkProcessingMetadata={dynamicSamplingContext:i,...t.sdkProcessingMetadata}}return t}))}_captureEvent(t,e={},n){return this._processEvent(t,e,n).then((t=>t.event_id),(t=>{if(G){const e=t;"log"===e.logLevel?a.log(e.message):a.warn(e)}}))}_processEvent(t,e,n){const r=this.getOptions(),{sampleRate:o}=r,s=dn(t),i=pn(t),c=t.type||"error",u=`before send for type \`${c}\``,l=void 0===o?void 0:function(t){if("boolean"==typeof t)return Number(t);const e="string"==typeof t?parseFloat(t):t;if(!("number"!=typeof e||isNaN(e)||e<0||e>1))return e;G&&a.warn(`[Tracing] Given sample rate is invalid. Sample rate must be a boolean or a number between 0 and 1. Got ${JSON.stringify(t)} of type ${JSON.stringify(typeof t)}.`)}(o);if(i&&"number"==typeof l&&Math.random()>l)return this.recordDroppedEvent("sample_rate","error",t),Gt(new an(`Discarding event because it's not included in the random sample (sampling rate = ${o})`,"log"));const p="replay_event"===c?"replay":c,d=(t.sdkProcessingMetadata||{}).capturedSpanIsolationScope;return this._prepareEvent(t,e,n,d).then((n=>{if(null===n)throw this.recordDroppedEvent("event_processor",p,t),new an("An event processor returned `null`, will not send event.","log");if(e.data&&!0===e.data.__sentry__)return n;const o=function(t,e,n,r){const{beforeSend:o,beforeSendTransaction:s,beforeSendSpan:i}=e;if(pn(n)&&o)return o(n,r);if(dn(n)){if(n.spans&&i){const e=[];for(const r of n.spans){const n=i(r);n?e.push(n):t.recordDroppedEvent("before_send","span")}n.spans=e}if(s){if(n.spans){const t=n.spans.length;n.sdkProcessingMetadata={...n.sdkProcessingMetadata,spanCountBeforeProcessing:t}}return s(n,r)}}return n}(this,r,n,e);return function(t,e){const n=`${e} must return \`null\` or a valid event.`;if(y(t))return t.then((t=>{if(!_(t)&&null!==t)throw new an(n);return t}),(t=>{throw new an(`${e} rejected with ${t}`)}));if(!_(t)&&null!==t)throw new an(n);return t}(o,u)})).then((r=>{if(null===r){if(this.recordDroppedEvent("before_send",p,t),s){const e=1+(t.spans||[]).length;this.recordDroppedEvent("before_send","span",e)}throw new an(`${u} returned \`null\`, will not send event.`,"log")}const o=n&&n.getSession();if(!s&&o&&this._updateSessionFromEvent(o,r),s){const t=(r.sdkProcessingMetadata&&r.sdkProcessingMetadata.spanCountBeforeProcessing||0)-(r.spans?r.spans.length:0);t>0&&this.recordDroppedEvent("before_send","span",t)}const i=r.transaction_info;if(s&&i&&r.transaction!==t.transaction){const t="custom";r.transaction_info={...i,source:t}}return this.sendEvent(r,e),r})).then(null,(t=>{if(t instanceof an)throw t;throw this.captureException(t,{data:{__sentry__:!0},originalException:t}),new an(`Event processing pipeline threw an error, original event will not be sent. Details have been sent as a new event.\nReason: ${t}`)}))}_process(t){this._numProcessing++,t.then((t=>(this._numProcessing--,t)),(t=>(this._numProcessing--,t)))}_clearOutcomes(){const t=this._outcomes;return this._outcomes={},Object.entries(t).map((([t,e])=>{const[n,r]=t.split(":");return{reason:n,category:r,quantity:e}}))}_flushOutcomes(){G&&a.log("Flushing outcomes...");const t=this._clearOutcomes();if(0===t.length)return void(G&&a.log("No outcomes to send"));if(!this._dsn)return void(G&&a.log("No dsn provided, will not send outcomes"));G&&a.log("Sending outcomes:",t);const e=(n=t,Xe((r=this._options.tunnel&&Ie(this._dsn))?{dsn:r}:{},[[{type:"client_report"},{timestamp:o||rt(),discarded_events:n}]]));var n,r,o;this.sendEnvelope(e)}}function pn(t){return void 0===t.type}function dn(t){return"transaction"===t.type}const fn="undefined"==typeof __SENTRY_DEBUG__||__SENTRY_DEBUG__;function hn(t,e){const n=gn(t,e),r={type:e&&e.name,value:vn(e)};return n.length&&(r.stacktrace={frames:n}),void 0===r.type&&""===r.value&&(r.value="Unrecoverable error caught"),r}function mn(t,e,n,r){const o=bt(),s=o&&o.getOptions().normalizeDepth,i=function(t){for(const e in t)if(Object.prototype.hasOwnProperty.call(t,e)){const n=t[e];if(n instanceof Error)return n}return}(e),a={__serialized__:Ft(e,s)};if(i)return{exception:{values:[hn(t,i)]},extra:a};const c={exception:{values:[{type:g(e)?e.constructor.name:r?"UnhandledRejection":"Error",value:Sn(e,{isUnhandledRejection:r})}]},extra:a};if(n){const e=gn(t,n);e.length&&(c.exception.values[0].stacktrace={frames:e})}return c}function _n(t,e){return{exception:{values:[hn(t,e)]}}}function gn(t,e){const n=e.stacktrace||e.stack||"",r=function(t){if(t&&yn.test(t.message))return 1;return 0}(e),o=function(t){if("number"==typeof t.framesToPop)return t.framesToPop;return 0}(e);try{return t(n,r,o)}catch(t){}return[]}const yn=/Minified React error #\d+;/i;function vn(t){const e=t&&t.message;return e?e.error&&"string"==typeof e.error.message?e.error.message:e:"No error message"}function En(t,e,n,r,o){let s;if(p(e)&&e.error){return _n(t,e.error)}if(d(e)||l(e,"DOMException")){const o=e;if("stack"in e)s=_n(t,e);else{const e=o.name||(d(o)?"DOMError":"DOMException"),i=o.message?`${e}: ${o.message}`:e;s=bn(t,i,n,r),F(s,i)}return"code"in o&&(s.tags={...s.tags,"DOMException.code":`${o.code}`}),s}if(u(e))return _n(t,e);if(_(e)||g(e)){return s=mn(t,e,n,o),Y(s,{synthetic:!0}),s}return s=bn(t,e,n,r),F(s,`${e}`,void 0),Y(s,{synthetic:!0}),s}function bn(t,e,n,r){const o={};if(r&&n){const r=gn(t,n);r.length&&(o.exception={values:[{value:e,stacktrace:{frames:r}}]})}if(h(e)){const{__sentry_template_string__:t,__sentry_template_values__:n}=e;return o.logentry={message:t,params:n},o}return o.message=e,o}function Sn(t,{isUnhandledRejection:e}){const n=function(t,e=40){const n=Object.keys(R(t));n.sort();const r=n[0];if(!r)return"[object has no keys]";if(r.length>=e)return k(r,e);for(let t=n.length;t>0;t--){const r=n.slice(0,t).join(", ");if(!(r.length>e))return t===n.length?r:k(r,e)}return""}(t),r=e?"promise rejection":"exception";if(p(t))return`Event \`ErrorEvent\` captured as ${r} with message \`${t.message}\``;if(g(t)){return`Event \`${function(t){try{const e=Object.getPrototypeOf(t);return e?e.constructor.name:void 0}catch(t){}}(t)}\` (type=${t.type}) captured as ${r}`}return`Object captured as ${r} with keys: ${n}`}const xn=n;let wn=0;function kn(){return wn>0}function $n(t,e={},n){if("function"!=typeof t)return t;try{const e=t.__sentry_wrapped__;if(e)return e;if(P(t))return t}catch(e){return t}const r=function(){const r=Array.prototype.slice.call(arguments);try{n&&"function"==typeof n&&n.apply(this,arguments);const o=r.map((t=>$n(t,e)));return t.apply(this,o)}catch(t){throw wn++,setTimeout((()=>{wn--})),function(...t){const e=yt(Z());if(2===t.length){const[n,r]=t;return n?e.withSetScope(n,r):e.withScope(r)}e.withScope(t[0])}((n=>{var o,s;n.addEventProcessor((t=>(e.mechanism&&(F(t,void 0,void 0),Y(t,e.mechanism)),t.extra={...t.extra,arguments:r},t))),o=t,vt().captureException(o,xe(s))})),t}};try{for(const e in t)Object.prototype.hasOwnProperty.call(t,e)&&(r[e]=t[e])}catch(t){}I(r,t),N(t,"__sentry_wrapped__",r);try{Object.getOwnPropertyDescriptor(r,"name").configurable&&Object.defineProperty(r,"name",{get:()=>t.name})}catch(t){}return r}class On extends ln{constructor(t){const n={parentSpanIsAlwaysRootSpan:!0,...t};!function(t,n,r=[n],o="npm"){const s=t._metadata||{};s.sdk||(s.sdk={name:`sentry.javascript.${n}`,packages:r.map((t=>({name:`${o}:@sentry/${t}`,version:e}))),version:e}),t._metadata=s}(n,"browser",["browser"],xn.SENTRY_SDK_SOURCE||"npm"),super(n),n.sendClientReports&&xn.document&&xn.document.addEventListener("visibilitychange",(()=>{"hidden"===xn.document.visibilityState&&this._flushOutcomes()}))}eventFromException(t,e){return function(t,e,n,r){const o=En(t,e,n&&n.syntheticException||void 0,r);return Y(o),o.level="error",n&&n.event_id&&(o.event_id=n.event_id),Ht(o)}(this._options.stackParser,t,e,this._options.attachStacktrace)}eventFromMessage(t,e="info",n){return function(t,e,n="info",r,o){const s=bn(t,e,r&&r.syntheticException||void 0,o);return s.level=n,r&&r.event_id&&(s.event_id=r.event_id),Ht(s)}(this._options.stackParser,t,e,n,this._options.attachStacktrace)}captureUserFeedback(t){if(!this._isEnabled())return void(fn&&a.warn("SDK not enabled, will not capture user feedback."));const e=function(t,{metadata:e,tunnel:n,dsn:r}){const o={event_id:t.event_id,sent_at:(new Date).toISOString(),...e&&e.sdk&&{sdk:{name:e.sdk.name,version:e.sdk.version}},...!!n&&!!r&&{dsn:Ie(r)}},s=function(t){return[{type:"user_report"},t]}(t);return Xe(o,[s])}(t,{metadata:this.getSdkMetadata(),dsn:this.getDsn(),tunnel:this.getOptions().tunnel});this.sendEnvelope(e)}_prepareEvent(t,e,n){return t.platform=t.platform||"javascript",super._prepareEvent(t,e,n)}}const Tn=1e3;let Dn,Nn,In;function Pn(){if(!ze.document)return;const t=Ge.bind(null,"dom"),e=Rn(t,!0);ze.document.addEventListener("click",e,!1),ze.document.addEventListener("keypress",e,!1),["EventTarget","Node"].forEach((e=>{const n=ze[e]&&ze[e].prototype;n&&n.hasOwnProperty&&n.hasOwnProperty("addEventListener")&&(D(n,"addEventListener",(function(e){return function(n,r,o){if("click"===n||"keypress"==n)try{const r=this,s=r.__sentry_instrumentation_handlers__=r.__sentry_instrumentation_handlers__||{},i=s[n]=s[n]||{refCount:0};if(!i.handler){const r=Rn(t);i.handler=r,e.call(this,n,r,o)}i.refCount++}catch(t){}return e.call(this,n,r,o)}})),D(n,"removeEventListener",(function(t){return function(e,n,r){if("click"===e||"keypress"==e)try{const n=this,o=n.__sentry_instrumentation_handlers__||{},s=o[e];s&&(s.refCount--,s.refCount<=0&&(t.call(this,e,s.handler,r),s.handler=void 0,delete o[e]),0===Object.keys(o).length&&delete n.__sentry_instrumentation_handlers__)}catch(t){}return t.call(this,e,n,r)}})))}))}function Rn(t,e=!1){return n=>{if(!n||n._sentryCaptured)return;const r=function(t){try{return t.target}catch(t){return null}}(n);if(function(t,e){return"keypress"===t&&(!e||!e.tagName||"INPUT"!==e.tagName&&"TEXTAREA"!==e.tagName&&!e.isContentEditable)}(n.type,r))return;N(n,"_sentryCaptured",!0),r&&!r._sentryId&&N(r,"_sentryId",M());const o="keypress"===n.type?"input":n.type;if(!function(t){if(t.type!==Nn)return!1;try{if(!t.target||t.target._sentryId!==In)return!1}catch(t){}return!0}(n)){t({event:n,name:o,global:e}),Nn=n.type,In=r?r._sentryId:void 0}clearTimeout(Dn),Dn=ze.setTimeout((()=>{In=void 0,Nn=void 0}),Tn)}}const Cn="__sentry_xhr_v3__";function jn(){if(!ze.XMLHttpRequest)return;const t=XMLHttpRequest.prototype;t.open=new Proxy(t.open,{apply(t,e,n){const r=1e3*ot(),o=f(n[0])?n[0].toUpperCase():void 0,s=function(t){if(f(t))return t;try{return t.toString()}catch(t){}return}(n[1]);if(!o||!s)return t.apply(e,n);e[Cn]={method:o,url:s,request_headers:{}},"POST"===o&&s.match(/sentry_key/)&&(e.__sentry_own_request__=!0);const i=()=>{const t=e[Cn];if(t&&4===e.readyState){try{t.status_code=e.status}catch(t){}Ge("xhr",{endTimestamp:1e3*ot(),startTimestamp:r,xhr:e})}};return"onreadystatechange"in e&&"function"==typeof e.onreadystatechange?e.onreadystatechange=new Proxy(e.onreadystatechange,{apply:(t,e,n)=>(i(),t.apply(e,n))}):e.addEventListener("readystatechange",i),e.setRequestHeader=new Proxy(e.setRequestHeader,{apply(t,e,n){const[r,o]=n,s=e[Cn];return s&&f(r)&&f(o)&&(s.request_headers[r.toLowerCase()]=o),t.apply(e,n)}}),t.apply(e,n)}}),t.send=new Proxy(t.send,{apply(t,e,n){const r=e[Cn];if(!r)return t.apply(e,n);void 0!==n[0]&&(r.body=n[0]);return Ge("xhr",{startTimestamp:1e3*ot(),xhr:e}),t.apply(e,n)}})}const An=100;function Ln(t,e){const n=bt(),r=Et();if(!n)return;const{beforeBreadcrumb:o=null,maxBreadcrumbs:s=An}=n.getOptions();if(s<=0)return;const a={timestamp:rt(),...t},c=o?i((()=>o(a,e))):a;null!==c&&(n.emit&&n.emit("beforeAddBreadcrumb",c,e),r.addBreadcrumb(c,s))}function Mn(){"console"in n&&o.forEach((function(t){t in n.console&&D(n.console,t,(function(e){return s[t]=e,function(...e){Ge("console",{args:e,level:t});const r=s[t];r&&r.apply(n.console,e)}}))}))}function Un(e,r=!1){r&&!function(){if("string"==typeof EdgeRuntime)return!0;if(!Ue())return!1;if(qe(Me.fetch))return!0;let e=!1;const n=Me.document;if(n&&"function"==typeof n.createElement)try{const t=n.createElement("iframe");t.hidden=!0,n.head.appendChild(t),t.contentWindow&&t.contentWindow.fetch&&(e=qe(t.contentWindow.fetch)),n.head.removeChild(t)}catch(e){t&&a.warn("Could not create sandbox iframe for pure fetch check, bailing to window.fetch: ",e)}return e}()||D(n,"fetch",(function(t){return function(...r){const{method:o,url:s}=function(t){if(0===t.length)return{method:"GET",url:""};if(2===t.length){const[e,n]=t;return{url:Fn(e),method:qn(n,"method")?String(n.method).toUpperCase():"GET"}}const e=t[0];return{url:Fn(e),method:qn(e,"method")?String(e.method).toUpperCase():"GET"}}(r),i={args:r,fetchData:{method:o,url:s},startTimestamp:1e3*ot()};e||Ge("fetch",{...i});const a=(new Error).stack;return t.apply(n,r).then((async t=>(e?e(t):Ge("fetch",{...i,endTimestamp:1e3*ot(),response:t}),t)),(t=>{throw Ge("fetch",{...i,endTimestamp:1e3*ot(),error:t}),u(t)&&void 0===t.stack&&(t.stack=a,N(t,"framesToPop",1)),t}))}}))}function qn(t,e){return!!t&&"object"==typeof t&&!!t[e]}function Fn(t){return"string"==typeof t?t:t?qn(t,"url")?t.url:t.toString?t.toString():"":""}const Yn=["fatal","error","warning","log","info","debug"];function Bn(t){return"warn"===t?"warning":Yn.includes(t)?t:"log"}function Hn(t){return void 0===t?void 0:t>=400&&t<500?"warning":t>=500?"error":void 0}function Gn(t){if(!t)return{};const e=t.match(/^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/);if(!e)return{};const n=e[6]||"",r=e[8]||"";return{host:e[4],path:e[5],protocol:e[2],search:n,hash:r,relative:e[5]+n+r}}const Wn=1024,zn=(t={})=>{const e={console:!0,dom:!0,fetch:!0,history:!0,sentry:!0,xhr:!0,...t};return{name:"Breadcrumbs",setup(t){var n;e.console&&function(t){const e="console";Be(e,t),He(e,Mn)}(function(t){return function(e){if(bt()!==t)return;const n={category:"console",data:{arguments:e.args,logger:"console"},level:Bn(e.level),message:$(e.args," ")};if("assert"===e.level){if(!1!==e.args[0])return;n.message=`Assertion failed: ${$(e.args.slice(1)," ")||"console.assert"}`,n.data.arguments=e.args.slice(1)}Ln(n,{input:e.args,level:e.level})}}(t)),e.dom&&(n=function(t,e){return function(n){if(bt()!==t)return;let r,o,s="object"==typeof e?e.serializeAttribute:void 0,i="object"==typeof e&&"number"==typeof e.maxStringLength?e.maxStringLength:void 0;i&&i>Wn&&(fn&&a.warn(`\`dom.maxStringLength\` cannot exceed 1024, but a value of ${i} was configured. Sentry will use 1024 instead.`),i=Wn),"string"==typeof s&&(s=[s]);try{const t=n.event,e=function(t){return!!t&&!!t.target}(t)?t.target:t;r=x(e,{keyAttrs:s,maxStringLength:i}),o=function(t){if(!b.HTMLElement)return null;let e=t;for(let t=0;t<5;t++){if(!e)return null;if(e instanceof HTMLElement){if(e.dataset.sentryComponent)return e.dataset.sentryComponent;if(e.dataset.sentryElement)return e.dataset.sentryElement}e=e.parentNode}return null}(e)}catch(t){r=""}if(0===r.length)return;const c={category:`ui.${n.name}`,message:r};o&&(c.data={"ui.component_name":o}),Ln(c,{event:n.event,name:n.name,global:n.global})}}(t,e.dom),Be("dom",n),He("dom",Pn)),e.xhr&&function(t){Be("xhr",t),He("xhr",jn)}(function(t){return function(e){if(bt()!==t)return;const{startTimestamp:n,endTimestamp:r}=e,o=e.xhr[Cn];if(!n||!r||!o)return;const{method:s,url:i,status_code:a,body:c}=o,u={method:s,url:i,status_code:a},l={xhr:e.xhr,input:c,startTimestamp:n,endTimestamp:r};Ln({category:"xhr",data:u,type:"http",level:Hn(a)},l)}}(t)),e.fetch&&function(t,e){const n="fetch";Be(n,t),He(n,(()=>Un(void 0,e)))}(function(t){return function(e){if(bt()!==t)return;const{startTimestamp:n,endTimestamp:r}=e;if(r&&(!e.fetchData.url.match(/sentry_key/)||"POST"!==e.fetchData.method))if(e.error){Ln({category:"fetch",data:e.fetchData,level:"error",type:"http"},{data:e.error,input:e.args,startTimestamp:n,endTimestamp:r})}else{const t=e.response,o={...e.fetchData,status_code:t&&t.status},s={input:e.args,response:t,startTimestamp:n,endTimestamp:r};Ln({category:"fetch",data:o,type:"http",level:Hn(o.status_code)},s)}}}(t)),e.history&&Ve(function(t){return function(e){if(bt()!==t)return;let n=e.from,r=e.to;const o=Gn(xn.location.href);let s=n?Gn(n):void 0;const i=Gn(r);s&&s.path||(s=o),o.protocol===i.protocol&&o.host===i.host&&(r=i.relative),o.protocol===s.protocol&&o.host===s.host&&(n=s.relative),Ln({category:"navigation",data:{from:n,to:r}})}}(t)),e.sentry&&t.on("beforeSendEvent",function(t){return function(e){bt()===t&&Ln({category:"sentry."+("transaction"===e.type?"transaction":"event"),event_id:e.event_id,level:e.level,message:q(e)},{event:e})}}(t))}}};const Jn=["EventTarget","Window","Node","ApplicationCache","AudioTrackList","BroadcastChannel","ChannelMergerNode","CryptoOperation","EventSource","FileReader","HTMLUnknownElement","IDBDatabase","IDBRequest","IDBTransaction","KeyOperation","MediaController","MessagePort","ModalWindow","Notification","SVGElementInstance","Screen","SharedWorker","TextTrack","TextTrackCue","TextTrackList","WebSocket","WebSocketWorker","Worker","XMLHttpRequest","XMLHttpRequestEventTarget","XMLHttpRequestUpload"],Vn=(t={})=>{const e={XMLHttpRequest:!0,eventTarget:!0,requestAnimationFrame:!0,setInterval:!0,setTimeout:!0,...t};return{name:"BrowserApiErrors",setupOnce(){e.setTimeout&&D(xn,"setTimeout",Kn),e.setInterval&&D(xn,"setInterval",Kn),e.requestAnimationFrame&&D(xn,"requestAnimationFrame",Xn),e.XMLHttpRequest&&"XMLHttpRequest"in xn&&D(XMLHttpRequest.prototype,"send",Qn);const t=e.eventTarget;if(t){(Array.isArray(t)?t:Jn).forEach(Zn)}}}};function Kn(t){return function(...e){const n=e[0];return e[0]=$n(n,{mechanism:{data:{function:Pt(t)},handled:!1,type:"instrument"}}),t.apply(this,e)}}function Xn(t){return function(e){return t.apply(this,[$n(e,{mechanism:{data:{function:"requestAnimationFrame",handler:Pt(t)},handled:!1,type:"instrument"}})])}}function Qn(t){return function(...e){const n=this;return["onload","onerror","onprogress","onreadystatechange"].forEach((t=>{t in n&&"function"==typeof n[t]&&D(n,t,(function(e){const n={mechanism:{data:{function:t,handler:Pt(e)},handled:!1,type:"instrument"}},r=P(e);return r&&(n.mechanism.data.handler=Pt(r)),$n(e,n)}))})),t.apply(this,e)}}function Zn(t){const e=xn,n=e[t]&&e[t].prototype;n&&n.hasOwnProperty&&n.hasOwnProperty("addEventListener")&&(D(n,"addEventListener",(function(e){return function(n,r,o){try{"function"==typeof r.handleEvent&&(r.handleEvent=$n(r.handleEvent,{mechanism:{data:{function:"handleEvent",handler:Pt(r),target:t},handled:!1,type:"instrument"}}))}catch(t){}return e.apply(this,[n,$n(r,{mechanism:{data:{function:"addEventListener",handler:Pt(r),target:t},handled:!1,type:"instrument"}}),o])}})),D(n,"removeEventListener",(function(t){return function(e,n,r){const o=n;try{const n=o&&o.__sentry_wrapped__;n&&t.call(this,e,n,r)}catch(t){}return t.call(this,e,o,r)}})))}let tr=null;function er(){tr=n.onerror,n.onerror=function(t,e,n,r,o){return Ge("error",{column:r,error:o,line:n,msg:t,url:e}),!(!tr||tr.__SENTRY_LOADER__)&&tr.apply(this,arguments)},n.onerror.__SENTRY_INSTRUMENTED__=!0}let nr=null;function rr(){nr=n.onunhandledrejection,n.onunhandledrejection=function(t){return Ge("unhandledrejection",t),!(nr&&!nr.__SENTRY_LOADER__)||nr.apply(this,arguments)},n.onunhandledrejection.__SENTRY_INSTRUMENTED__=!0}const or=(t={})=>{const e={onerror:!0,onunhandledrejection:!0,...t};return{name:"GlobalHandlers",setupOnce(){Error.stackTraceLimit=50},setup(t){e.onerror&&(!function(t){!function(t){const e="error";Be(e,t),He(e,er)}((e=>{const{stackParser:n,attachStacktrace:r}=ir();if(bt()!==t||kn())return;const{msg:o,url:s,line:i,column:a,error:c}=e,u=function(t,e,n,r){const o=t.exception=t.exception||{},s=o.values=o.values||[],i=s[0]=s[0]||{},a=i.stacktrace=i.stacktrace||{},c=a.frames=a.frames||[],u=isNaN(parseInt(r,10))?void 0:r,l=isNaN(parseInt(n,10))?void 0:n,p=f(e)&&e.length>0?e:function(){try{return b.document.location.href}catch(t){return""}}();0===c.length&&c.push({colno:u,filename:p,function:$t,in_app:!0,lineno:l});return t}(En(n,c||o,void 0,r,!1),s,i,a);u.level="error",ke(u,{originalException:c,mechanism:{handled:!1,type:"onerror"}})}))}(t),sr("onerror")),e.onunhandledrejection&&(!function(t){!function(t){const e="unhandledrejection";Be(e,t),He(e,rr)}((e=>{const{stackParser:n,attachStacktrace:r}=ir();if(bt()!==t||kn())return;const o=function(t){if(m(t))return t;try{if("reason"in t)return t.reason;if("detail"in t&&"reason"in t.detail)return t.detail.reason}catch(t){}return t}(e),s=m(o)?{exception:{values:[{type:"UnhandledRejection",value:`Non-Error promise rejection captured with value: ${String(o)}`}]}}:En(n,o,void 0,r,!0);s.level="error",ke(s,{originalException:o,mechanism:{handled:!1,type:"onunhandledrejection"}})}))}(t),sr("onunhandledrejection"))}}};function sr(t){fn&&a.log(`Global Handler attached: ${t}`)}function ir(){const t=bt();return t&&t.getOptions()||{stackParser:()=>[],attachStacktrace:!1}}const ar=()=>({name:"HttpContext",preprocessEvent(t){if(!xn.navigator&&!xn.location&&!xn.document)return;const e=t.request&&t.request.url||xn.location&&xn.location.href,{referrer:n}=xn.document||{},{userAgent:r}=xn.navigator||{},o={...t.request&&t.request.headers,...n&&{Referer:n},...r&&{"User-Agent":r}},s={...t.request,...e&&{url:e},headers:o};t.request=s}});function cr(t,e,n=250,r,o,s,i){if(!(s.exception&&s.exception.values&&i&&v(i.originalException,Error)))return;const a=s.exception.values.length>0?s.exception.values[s.exception.values.length-1]:void 0;var c,u;a&&(s.exception.values=(c=ur(t,e,o,i.originalException,r,s.exception.values,a,0),u=n,c.map((t=>(t.value&&(t.value=k(t.value,u)),t)))))}function ur(t,e,n,r,o,s,i,a){if(s.length>=n+1)return s;let c=[...s];if(v(r[o],Error)){lr(i,a);const s=t(e,r[o]),u=c.length;pr(s,o,u,a),c=ur(t,e,n,r[o],o,[s,...c],s,u)}return Array.isArray(r.errors)&&r.errors.forEach(((r,s)=>{if(v(r,Error)){lr(i,a);const u=t(e,r),l=c.length;pr(u,`errors[${s}]`,l,a),c=ur(t,e,n,r,o,[u,...c],u,l)}})),c}function lr(t,e){t.mechanism=t.mechanism||{type:"generic",handled:!0},t.mechanism={...t.mechanism,..."AggregateError"===t.type&&{is_exception_group:!0},exception_id:e}}function pr(t,e,n,r){t.mechanism=t.mechanism||{type:"generic",handled:!0},t.mechanism={...t.mechanism,type:"chained",source:e,exception_id:n,parent_id:r}}const dr=(t={})=>{const e=t.limit||5,n=t.key||"cause";return{name:"LinkedErrors",preprocessEvent(t,r,o){const s=o.getOptions();cr(hn,s.stackParser,s.maxValueLength,n,e,t,r)}}};function fr(t,e,n,r){const o={filename:t,function:""===e?$t:e,in_app:!0};return void 0!==n&&(o.lineno=n),void 0!==r&&(o.colno=r),o}const hr=/^\s*at (\S+?)(?::(\d+))(?::(\d+))\s*$/i,mr=/^\s*at (?:(.+?\)(?: \[.+\])?|.*?) ?\((?:address at )?)?(?:async )?((?:|[-a-z]+:|.*bundle|\/)?.*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,_r=/\((\S*)(?::(\d+))(?::(\d+))\)/,gr=/^\s*(.*?)(?:\((.*?)\))?(?:^|@)?((?:[-a-z]+)?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js)|\/[\w\-. /=]+)(?::(\d+))?(?::(\d+))?\s*$/i,yr=/(\S+) line (\d+)(?: > eval line \d+)* > eval/i,vr=Dt(...[[30,t=>{const e=hr.exec(t);if(e){const[,t,n,r]=e;return fr(t,$t,+n,+r)}const n=mr.exec(t);if(n){if(n[2]&&0===n[2].indexOf("eval")){const t=_r.exec(n[2]);t&&(n[2]=t[1],n[3]=t[2],n[4]=t[3])}const[t,e]=Er(n[1]||$t,n[2]);return fr(e,t,n[3]?+n[3]:void 0,n[4]?+n[4]:void 0)}}],[50,t=>{const e=gr.exec(t);if(e){if(e[3]&&e[3].indexOf(" > eval")>-1){const t=yr.exec(e[3]);t&&(e[1]=e[1]||"eval",e[3]=t[1],e[4]=t[2],e[5]="")}let t=e[3],n=e[1]||$t;return[n,t]=Er(n,t),fr(t,n,e[4]?+e[4]:void 0,e[5]?+e[5]:void 0)}}]]),Er=(t,e)=>{const n=-1!==t.indexOf("safari-extension"),r=-1!==t.indexOf("safari-web-extension");return n||r?[-1!==t.indexOf("@")?t.split("@")[0]:$t,n?`safari-extension:${e}`:`safari-web-extension:${e}`]:[t,e]},br="undefined"==typeof __SENTRY_DEBUG__||__SENTRY_DEBUG__,Sr={};function xr(t){const e=Sr[t];if(e)return e;let n=ze[t];if(qe(n))return Sr[t]=n.bind(ze);const r=ze.document;if(r&&"function"==typeof r.createElement)try{const e=r.createElement("iframe");e.hidden=!0,r.head.appendChild(e);const o=e.contentWindow;o&&o[t]&&(n=o[t]),r.head.removeChild(e)}catch(e){br&&a.warn(`Could not create sandbox iframe for ${t} check, bailing to window.${t}: `,e)}return n?Sr[t]=n.bind(ze):n}function wr(t){Sr[t]=void 0}function kr(t){const e=[];function n(t){return e.splice(e.indexOf(t),1)[0]||Promise.resolve(void 0)}return{$:e,add:function(r){if(!(void 0===t||e.lengthn(o))).then(null,(()=>n(o).then(null,(()=>{})))),o},drain:function(t){return new Wt(((n,r)=>{let o=e.length;if(!o)return n(!0);const s=setTimeout((()=>{t&&t>0&&n(!1)}),t);e.forEach((t=>{Ht(t).then((()=>{--o||(clearTimeout(s),n(!0))}),r)}))}))}}}const $r=6e4;function Or(t,{statusCode:e,headers:n},r=Date.now()){const o={...t},s=n&&n["x-sentry-rate-limits"],i=n&&n["retry-after"];if(s)for(const t of s.trim().split(",")){const[e,n,,,s]=t.split(":",5),i=parseInt(e,10),a=1e3*(isNaN(i)?60:i);if(n)for(const t of n.split(";"))"metric_bucket"===t&&s&&!s.split(";").includes("custom")||(o[t]=r+a);else o.all=r+a}else i?o.all=r+function(t,e=Date.now()){const n=parseInt(`${t}`,10);if(!isNaN(n))return 1e3*n;const r=Date.parse(`${t}`);return isNaN(r)?$r:r-e}(i,r):429===e&&(o.all=r+6e4);return o}const Tr=64;function Dr(t,e,n=kr(t.bufferSize||Tr)){let r={};return{send:function(o){const s=[];if(Ze(o,((e,n)=>{const o=on(n);if(function(t,e,n=Date.now()){return function(t,e){return t[e]||t.all||0}(t,e)>n}(r,o)){const r=Nr(e,n);t.recordDroppedEvent("ratelimit_backoff",o,r)}else s.push(e)})),0===s.length)return Ht({});const i=Xe(o[0],s),c=e=>{Ze(i,((n,r)=>{const o=Nr(n,r);t.recordDroppedEvent(e,on(r),o)}))};return n.add((()=>e({body:en(i)}).then((t=>(void 0!==t.statusCode&&(t.statusCode<200||t.statusCode>=300)&&G&&a.warn(`Sentry responded with status code ${t.statusCode} to sent event.`),r=Or(r,t),t)),(t=>{throw c("network_error"),t})))).then((t=>t),(t=>{if(t instanceof an)return G&&a.error("Skipped sending event because buffer is full."),c("queue_overflow"),Ht({});throw t}))},flush:t=>n.drain(t)}}function Nr(t,e){if("event"===e||"transaction"===e)return Array.isArray(t)?t[1]:void 0}function Ir(t,e=xr("fetch")){let n=0,r=0;return Dr(t,(function(o){const s=o.body.length;n+=s,r++;const i={body:o.body,method:"POST",referrerPolicy:"origin",headers:t.headers,keepalive:n<=6e4&&r<15,...t.fetchOptions};if(!e)return wr("fetch"),Gt("No fetch implementation available");try{return e(t.url,i).then((t=>(n-=s,r--,{statusCode:t.status,headers:{"x-sentry-rate-limits":t.headers.get("X-Sentry-Rate-Limits"),"retry-after":t.headers.get("Retry-After")}})))}catch(t){return wr("fetch"),n-=s,r--,Gt(t)}}))}const Pr={init:function(t={}){const e=function(t={}){const e={defaultIntegrations:[X(),wt(),Vn(),zn(),or(),dr(),Ct(),ar()],release:"string"==typeof __SENTRY_RELEASE__?__SENTRY_RELEASE__:xn.SENTRY_RELEASE&&xn.SENTRY_RELEASE.id?xn.SENTRY_RELEASE.id:void 0,autoSessionTracking:!0,sendClientReports:!0};return null==t.defaultIntegrations&&delete t.defaultIntegrations,{...e,...t}}(t);if(function(){const t=void 0!==xn.window&&xn;if(!t)return!1;const e=t[t.chrome?"chrome":"browser"],n=e&&e.runtime&&e.runtime.id,r=xn.location&&xn.location.href||"",o=!!n&&xn===xn.top&&["chrome-extension:","moz-extension:","ms-browser-extension:","safari-web-extension:"].some((t=>r.startsWith(`${t}//`))),s=void 0!==t.nw;return!!n&&!o&&!s}())return void i((()=>{console.error("[Sentry] You cannot run Sentry this way in a browser extension, check: https://docs.sentry.io/platforms/javascript/best-practices/browser-extensions/")}));fn&&(Ue()||a.warn("No Fetch API detected. The Sentry SDK requires a Fetch API compatible environment to send events. Please add a Fetch API polyfill."));const n={...e,stackParser:(r=e.stackParser||vr,Array.isArray(r)?Dt(...r):r),integrations:z(e),transport:e.transport||Ir};var r;const o=Mt(On,n);return e.autoSessionTracking&&function(){if(void 0===xn.document)return void(fn&&a.warn("Session tracking in non-browser environment with @sentry/browser is not supported."));$e({ignoreDuration:!0}),De(),Ve((({from:t,to:e})=>{void 0!==t&&t!==e&&($e({ignoreDuration:!0}),De())}))}(),o},showReportDialog:function(t={}){if(!xn.document)return void(fn&&a.error("Global document not defined in showReportDialog call"));const e=vt(),n=e.getClient(),r=n&&n.getDsn();if(!r)return void(fn&&a.error("DSN not configured for showReportDialog call"));if(e&&(t.user={...e.getUser(),...t.user}),!t.eventId){const e=Et().lastEventId();e&&(t.eventId=e)}const o=xn.document.createElement("script");o.async=!0,o.crossOrigin="anonymous",o.src=function(t,e){const n=Re(t);if(!n)return"";const r=`${je(n)}embed/error-page/`;let o=`dsn=${Ie(n)}`;for(const t in e)if("dsn"!==t&&"onClose"!==t)if("user"===t){const t=e.user;if(!t)continue;t.name&&(o+=`&name=${encodeURIComponent(t.name)}`),t.email&&(o+=`&email=${encodeURIComponent(t.email)}`)}else o+=`&${encodeURIComponent(t)}=${encodeURIComponent(e[t])}`;return`${r}?${o}`}(r,t),t.onLoad&&(o.onload=t.onLoad);const{onClose:s}=t;if(s){const t=e=>{if("__sentry_reportdialog_closed__"===e.data)try{s()}finally{xn.removeEventListener("message",t)}};xn.addEventListener("message",t)}const i=xn.document.head||xn.document.body;i?i.appendChild(o):fn&&a.error("Not injecting report dialog. No injection point found in HTML")}};window.Sentry=Pr})(); \ No newline at end of file +(()=>{"use strict";const t="undefined"==typeof __SENTRY_DEBUG__||__SENTRY_DEBUG__,e="8.33.0",n=globalThis;function r(t,r,o){const s=o||n,i=s.__SENTRY__=s.__SENTRY__||{},a=i[e]=i[e]||{};return a[t]||(a[t]=r())}const o=["debug","info","warn","error","log","assert","trace"],s={};function i(t){if(!("console"in n))return t();const e=n.console,r={},o=Object.keys(s);o.forEach((t=>{const n=s[t];r[t]=e[t],e[t]=n}));try{return t()}finally{o.forEach((t=>{e[t]=r[t]}))}}const a=r("logger",(function(){let e=!1;const r={enable:()=>{e=!0},disable:()=>{e=!1},isEnabled:()=>e};return t?o.forEach((t=>{r[t]=(...r)=>{e&&i((()=>{n.console[t](`Sentry Logger [${t}]:`,...r)}))}})):o.forEach((t=>{r[t]=()=>{}})),r})),c=Object.prototype.toString;function u(t){switch(c.call(t)){case"[object Error]":case"[object Exception]":case"[object DOMException]":return!0;default:return v(t,Error)}}function l(t,e){return c.call(t)===`[object ${e}]`}function p(t){return l(t,"ErrorEvent")}function d(t){return l(t,"DOMError")}function f(t){return l(t,"String")}function h(t){return"object"==typeof t&&null!==t&&"__sentry_template_string__"in t&&"__sentry_template_values__"in t}function m(t){return null===t||h(t)||"object"!=typeof t&&"function"!=typeof t}function _(t){return l(t,"Object")}function g(t){return"undefined"!=typeof Event&&v(t,Event)}function y(t){return Boolean(t&&t.then&&"function"==typeof t.then)}function v(t,e){try{return t instanceof e}catch(t){return!1}}function E(t){return!("object"!=typeof t||null===t||!t.__isVue&&!t._isVue)}const b=n,S=80;function x(t,e={}){if(!t)return"";try{let n=t;const r=5,o=[];let s=0,i=0;const a=" > ",c=a.length;let u;const l=Array.isArray(e)?e:e.keyAttrs,p=!Array.isArray(e)&&e.maxStringLength||S;for(;n&&s++1&&i+o.length*c+u.length>=p));)o.push(u),i+=u.length,n=n.parentNode;return o.reverse().join(a)}catch(t){return""}}function w(t,e){const n=t,r=[];if(!n||!n.tagName)return"";if(b.HTMLElement&&n instanceof HTMLElement&&n.dataset){if(n.dataset.sentryComponent)return n.dataset.sentryComponent;if(n.dataset.sentryElement)return n.dataset.sentryElement}r.push(n.tagName.toLowerCase());const o=e&&e.length?e.filter((t=>n.getAttribute(t))).map((t=>[t,n.getAttribute(t)])):null;if(o&&o.length)o.forEach((t=>{r.push(`[${t[0]}="${t[1]}"]`)}));else{n.id&&r.push(`#${n.id}`);const t=n.className;if(t&&f(t)){const e=t.split(/\s+/);for(const t of e)r.push(`.${t}`)}}const s=["aria-label","type","name","title","alt"];for(const t of s){const e=n.getAttribute(t);e&&r.push(`[${t}="${e}"]`)}return r.join("")}function k(t,e=0){return"string"!=typeof t||0===e||t.length<=e?t:`${t.slice(0,e)}...`}function $(t,e){if(!Array.isArray(t))return"";const n=[];for(let e=0;eO(t,e,n)))}function D(t,e,n){if(!(e in t))return;const r=t[e],o=n(r);"function"==typeof o&&I(o,r),t[e]=o}function N(e,n,r){try{Object.defineProperty(e,n,{value:r,writable:!0,configurable:!0})}catch(r){t&&a.log(`Failed to add non-enumerable property "${n}" to object`,e)}}function I(t,e){try{const n=e.prototype||{};t.prototype=e.prototype=n,N(t,"__sentry_original__",e)}catch(t){}}function P(t){return t.__sentry_original__}function R(t){if(u(t))return{message:t.message,name:t.name,stack:t.stack,...j(t)};if(g(t)){const e={type:t.type,target:C(t.target),currentTarget:C(t.currentTarget),...j(t)};return"undefined"!=typeof CustomEvent&&v(t,CustomEvent)&&(e.detail=t.detail),e}return t}function C(t){try{return e=t,"undefined"!=typeof Element&&v(e,Element)?x(t):Object.prototype.toString.call(t)}catch(t){return""}var e}function j(t){if("object"==typeof t&&null!==t){const e={};for(const n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e}return{}}function A(t){return L(t,new Map)}function L(t,e){if(function(t){if(!_(t))return!1;try{const e=Object.getPrototypeOf(t).constructor.name;return!e||"Object"===e}catch(t){return!0}}(t)){const n=e.get(t);if(void 0!==n)return n;const r={};e.set(t,r);for(const n of Object.getOwnPropertyNames(t))void 0!==t[n]&&(r[n]=L(t[n],e));return r}if(Array.isArray(t)){const n=e.get(t);if(void 0!==n)return n;const r=[];return e.set(t,r),t.forEach((t=>{r.push(L(t,e))})),r}return t}function M(){const t=n,e=t.crypto||t.msCrypto;let r=()=>16*Math.random();try{if(e&&e.randomUUID)return e.randomUUID().replace(/-/g,"");e&&e.getRandomValues&&(r=()=>{const t=new Uint8Array(1);return e.getRandomValues(t),t[0]})}catch(t){}return([1e7]+1e3+4e3+8e3+1e11).replace(/[018]/g,(t=>(t^(15&r())>>t/4).toString(16)))}function U(t){return t.exception&&t.exception.values?t.exception.values[0]:void 0}function q(t){const{message:e,event_id:n}=t;if(e)return e;const r=U(t);return r?r.type&&r.value?`${r.type}: ${r.value}`:r.type||r.value||n||"":n||""}function F(t,e,n){const r=t.exception=t.exception||{},o=r.values=r.values||[],s=o[0]=o[0]||{};s.value||(s.value=e||""),s.type||(s.type=n||"Error")}function Y(t,e){const n=U(t);if(!n)return;const r=n.mechanism;if(n.mechanism={type:"generic",handled:!0,...r,...e},e&&"data"in e){const t={...r&&r.data,...e.data};n.mechanism.data=t}}function B(t){if(t&&t.__sentry_captured__)return!0;try{N(t,"__sentry_captured__",!0)}catch(t){}return!1}function H(t){return Array.isArray(t)?t:[t]}const G="undefined"==typeof __SENTRY_DEBUG__||__SENTRY_DEBUG__,W=[];function z(t){const e=t.defaultIntegrations||[],n=t.integrations;let r;e.forEach((t=>{t.isDefaultInstance=!0})),r=Array.isArray(n)?[...e,...n]:"function"==typeof n?H(n(e)):e;const o=function(t){const e={};return t.forEach((t=>{const{name:n}=t,r=e[n];r&&!r.isDefaultInstance&&t.isDefaultInstance||(e[n]=t)})),Object.values(e)}(r),s=o.findIndex((t=>"Debug"===t.name));if(s>-1){const[t]=o.splice(s,1);o.push(t)}return o}function J(t,e){for(const n of e)n&&n.afterAllSetup&&n.afterAllSetup(t)}function V(t,e,n){if(n[e.name])G&&a.log(`Integration skipped because it was already installed: ${e.name}`);else{if(n[e.name]=e,-1===W.indexOf(e.name)&&"function"==typeof e.setupOnce&&(e.setupOnce(),W.push(e.name)),e.setup&&"function"==typeof e.setup&&e.setup(t),"function"==typeof e.preprocessEvent){const n=e.preprocessEvent.bind(e);t.on("preprocessEvent",((e,r)=>n(e,r,t)))}if("function"==typeof e.processEvent){const n=e.processEvent.bind(e),r=Object.assign(((e,r)=>n(e,r,t)),{id:e.name});t.addEventProcessor(r)}G&&a.log(`Integration installed: ${e.name}`)}}const K=[/^Script error\.?$/,/^Javascript error: Script error\.? on line 0$/,/^ResizeObserver loop completed with undelivered notifications.$/,/^Cannot redefine property: googletag$/,"undefined is not an object (evaluating 'a.L')",'can\'t redefine non-configurable property "solana"',"vv().getRestrictions is not a function. (In 'vv().getRestrictions(1,a)', 'vv().getRestrictions' is undefined)","Can't find variable: _AutofillCallbackHandler"],X=(t={})=>({name:"InboundFilters",processEvent(e,n,r){const o=r.getOptions(),s=function(t={},e={}){return{allowUrls:[...t.allowUrls||[],...e.allowUrls||[]],denyUrls:[...t.denyUrls||[],...e.denyUrls||[]],ignoreErrors:[...t.ignoreErrors||[],...e.ignoreErrors||[],...t.disableErrorDefaults?[]:K],ignoreTransactions:[...t.ignoreTransactions||[],...e.ignoreTransactions||[]],ignoreInternal:void 0===t.ignoreInternal||t.ignoreInternal}}(t,o);return function(t,e){if(e.ignoreInternal&&function(t){try{return"SentryError"===t.exception.values[0].type}catch(t){}return!1}(t))return G&&a.warn(`Event dropped due to being internal Sentry Error.\nEvent: ${q(t)}`),!0;if(function(t,e){if(t.type||!e||!e.length)return!1;return function(t){const e=[];t.message&&e.push(t.message);let n;try{n=t.exception.values[t.exception.values.length-1]}catch(t){}n&&n.value&&(e.push(n.value),n.type&&e.push(`${n.type}: ${n.value}`));return e}(t).some((t=>T(t,e)))}(t,e.ignoreErrors))return G&&a.warn(`Event dropped due to being matched by \`ignoreErrors\` option.\nEvent: ${q(t)}`),!0;if(function(t){if(t.type)return!1;if(!t.exception||!t.exception.values||0===t.exception.values.length)return!1;return!t.message&&!t.exception.values.some((t=>t.stacktrace||t.type&&"Error"!==t.type||t.value))}(t))return G&&a.warn(`Event dropped due to not having an error message, error type or stacktrace.\nEvent: ${q(t)}`),!0;if(function(t,e){if("transaction"!==t.type||!e||!e.length)return!1;const n=t.transaction;return!!n&&T(n,e)}(t,e.ignoreTransactions))return G&&a.warn(`Event dropped due to being matched by \`ignoreTransactions\` option.\nEvent: ${q(t)}`),!0;if(function(t,e){if(!e||!e.length)return!1;const n=Q(t);return!!n&&T(n,e)}(t,e.denyUrls))return G&&a.warn(`Event dropped due to being matched by \`denyUrls\` option.\nEvent: ${q(t)}.\nUrl: ${Q(t)}`),!0;if(!function(t,e){if(!e||!e.length)return!0;const n=Q(t);return!n||T(n,e)}(t,e.allowUrls))return G&&a.warn(`Event dropped due to not being matched by \`allowUrls\` option.\nEvent: ${q(t)}.\nUrl: ${Q(t)}`),!0;return!1}(e,s)?null:e}});function Q(t){try{let e;try{e=t.exception.values[0].stacktrace.frames}catch(t){}return e?function(t=[]){for(let e=t.length-1;e>=0;e--){const n=t[e];if(n&&""!==n.filename&&"[native code]"!==n.filename)return n.filename||null}return null}(e):null}catch(e){return G&&a.error(`Cannot extract url for event ${q(t)}`),null}}function Z(){return tt(n),n}function tt(t){const n=t.__SENTRY__=t.__SENTRY__||{};return n.version=n.version||e,n[e]=n[e]||{}}function et(){return{traceId:M(),spanId:M().substring(16)}}const nt=1e3;function rt(){return Date.now()/nt}const ot=function(){const{performance:t}=n;if(!t||!t.now)return rt;const e=Date.now()-t.now(),r=null==t.timeOrigin?e:t.timeOrigin;return()=>(r+t.now())/nt}();let st;(()=>{const{performance:t}=n;if(!t||!t.now)return void(st="none");const e=36e5,r=t.now(),o=Date.now(),s=t.timeOrigin?Math.abs(t.timeOrigin+r-o):e,i=sfunction(t){return A({sid:`${t.sid}`,init:t.init,started:new Date(1e3*t.started).toISOString(),timestamp:new Date(1e3*t.timestamp).toISOString(),status:t.status,errors:t.errors,did:"number"==typeof t.did||"string"==typeof t.did?`${t.did}`:void 0,duration:t.duration,abnormal_mechanism:t.abnormal_mechanism,attrs:{release:t.release,environment:t.environment,ip_address:t.ipAddress,user_agent:t.userAgent}})}(n)};return t&&at(n,t),n}function at(t,e={}){if(e.user&&(!t.ipAddress&&e.user.ip_address&&(t.ipAddress=e.user.ip_address),t.did||e.did||(t.did=e.user.id||e.user.email||e.user.username)),t.timestamp=e.timestamp||ot(),e.abnormal_mechanism&&(t.abnormal_mechanism=e.abnormal_mechanism),e.ignoreDuration&&(t.ignoreDuration=e.ignoreDuration),e.sid&&(t.sid=32===e.sid.length?e.sid:M()),void 0!==e.init&&(t.init=e.init),!t.did&&e.did&&(t.did=`${e.did}`),"number"==typeof e.started&&(t.started=e.started),t.ignoreDuration)t.duration=void 0;else if("number"==typeof e.duration)t.duration=e.duration;else{const e=t.timestamp-t.started;t.duration=e>=0?e:0}e.release&&(t.release=e.release),e.environment&&(t.environment=e.environment),!t.ipAddress&&e.ipAddress&&(t.ipAddress=e.ipAddress),!t.userAgent&&e.userAgent&&(t.userAgent=e.userAgent),"number"==typeof e.errors&&(t.errors=e.errors),e.status&&(t.status=e.status)}const ct="_sentrySpan";function ut(t,e){e?N(t,ct,e):delete t[ct]}function lt(t){return t[ct]}class pt{constructor(){this._notifyingListeners=!1,this._scopeListeners=[],this._eventProcessors=[],this._breadcrumbs=[],this._attachments=[],this._user={},this._tags={},this._extra={},this._contexts={},this._sdkProcessingMetadata={},this._propagationContext=et()}clone(){const t=new pt;return t._breadcrumbs=[...this._breadcrumbs],t._tags={...this._tags},t._extra={...this._extra},t._contexts={...this._contexts},t._user=this._user,t._level=this._level,t._session=this._session,t._transactionName=this._transactionName,t._fingerprint=this._fingerprint,t._eventProcessors=[...this._eventProcessors],t._requestSession=this._requestSession,t._attachments=[...this._attachments],t._sdkProcessingMetadata={...this._sdkProcessingMetadata},t._propagationContext={...this._propagationContext},t._client=this._client,t._lastEventId=this._lastEventId,ut(t,lt(this)),t}setClient(t){this._client=t}setLastEventId(t){this._lastEventId=t}getClient(){return this._client}lastEventId(){return this._lastEventId}addScopeListener(t){this._scopeListeners.push(t)}addEventProcessor(t){return this._eventProcessors.push(t),this}setUser(t){return this._user=t||{email:void 0,id:void 0,ip_address:void 0,username:void 0},this._session&&at(this._session,{user:t}),this._notifyScopeListeners(),this}getUser(){return this._user}getRequestSession(){return this._requestSession}setRequestSession(t){return this._requestSession=t,this}setTags(t){return this._tags={...this._tags,...t},this._notifyScopeListeners(),this}setTag(t,e){return this._tags={...this._tags,[t]:e},this._notifyScopeListeners(),this}setExtras(t){return this._extra={...this._extra,...t},this._notifyScopeListeners(),this}setExtra(t,e){return this._extra={...this._extra,[t]:e},this._notifyScopeListeners(),this}setFingerprint(t){return this._fingerprint=t,this._notifyScopeListeners(),this}setLevel(t){return this._level=t,this._notifyScopeListeners(),this}setTransactionName(t){return this._transactionName=t,this._notifyScopeListeners(),this}setContext(t,e){return null===e?delete this._contexts[t]:this._contexts[t]=e,this._notifyScopeListeners(),this}setSession(t){return t?this._session=t:delete this._session,this._notifyScopeListeners(),this}getSession(){return this._session}update(t){if(!t)return this;const e="function"==typeof t?t(this):t,[n,r]=e instanceof dt?[e.getScopeData(),e.getRequestSession()]:_(e)?[t,t.requestSession]:[],{tags:o,extra:s,user:i,contexts:a,level:c,fingerprint:u=[],propagationContext:l}=n||{};return this._tags={...this._tags,...o},this._extra={...this._extra,...s},this._contexts={...this._contexts,...a},i&&Object.keys(i).length&&(this._user=i),c&&(this._level=c),u.length&&(this._fingerprint=u),l&&(this._propagationContext=l),r&&(this._requestSession=r),this}clear(){return this._breadcrumbs=[],this._tags={},this._extra={},this._user={},this._contexts={},this._level=void 0,this._transactionName=void 0,this._fingerprint=void 0,this._requestSession=void 0,this._session=void 0,ut(this,void 0),this._attachments=[],this._propagationContext=et(),this._notifyScopeListeners(),this}addBreadcrumb(t,e){const n="number"==typeof e?e:100;if(n<=0)return this;const r={timestamp:rt(),...t},o=this._breadcrumbs;return o.push(r),this._breadcrumbs=o.length>n?o.slice(-n):o,this._notifyScopeListeners(),this}getLastBreadcrumb(){return this._breadcrumbs[this._breadcrumbs.length-1]}clearBreadcrumbs(){return this._breadcrumbs=[],this._notifyScopeListeners(),this}addAttachment(t){return this._attachments.push(t),this}clearAttachments(){return this._attachments=[],this}getScopeData(){return{breadcrumbs:this._breadcrumbs,attachments:this._attachments,contexts:this._contexts,tags:this._tags,extra:this._extra,user:this._user,level:this._level,fingerprint:this._fingerprint||[],eventProcessors:this._eventProcessors,propagationContext:this._propagationContext,sdkProcessingMetadata:this._sdkProcessingMetadata,transactionName:this._transactionName,span:lt(this)}}setSDKProcessingMetadata(t){return this._sdkProcessingMetadata={...this._sdkProcessingMetadata,...t},this}setPropagationContext(t){return this._propagationContext=t,this}getPropagationContext(){return this._propagationContext}captureException(t,e){const n=e&&e.event_id?e.event_id:M();if(!this._client)return a.warn("No client configured on scope - will not capture exception!"),n;const r=new Error("Sentry syntheticException");return this._client.captureException(t,{originalException:t,syntheticException:r,...e,event_id:n},this),n}captureMessage(t,e,n){const r=n&&n.event_id?n.event_id:M();if(!this._client)return a.warn("No client configured on scope - will not capture message!"),r;const o=new Error(t);return this._client.captureMessage(t,e,{originalException:t,syntheticException:o,...n,event_id:r},this),r}captureEvent(t,e){const n=e&&e.event_id?e.event_id:M();return this._client?(this._client.captureEvent(t,{...e,event_id:n},this),n):(a.warn("No client configured on scope - will not capture event!"),n)}_notifyScopeListeners(){this._notifyingListeners||(this._notifyingListeners=!0,this._scopeListeners.forEach((t=>{t(this)})),this._notifyingListeners=!1)}}const dt=pt;class ft{constructor(t,e){let n,r;n=t||new dt,r=e||new dt,this._stack=[{scope:n}],this._isolationScope=r}withScope(t){const e=this._pushScope();let n;try{n=t(e)}catch(t){throw this._popScope(),t}return y(n)?n.then((t=>(this._popScope(),t)),(t=>{throw this._popScope(),t})):(this._popScope(),n)}getClient(){return this.getStackTop().client}getScope(){return this.getStackTop().scope}getIsolationScope(){return this._isolationScope}getStackTop(){return this._stack[this._stack.length-1]}_pushScope(){const t=this.getScope().clone();return this._stack.push({client:this.getClient(),scope:t}),t}_popScope(){return!(this._stack.length<=1)&&!!this._stack.pop()}}function ht(){const t=tt(Z());return t.stack=t.stack||new ft(r("defaultCurrentScope",(()=>new dt)),r("defaultIsolationScope",(()=>new dt)))}function mt(t){return ht().withScope(t)}function _t(t,e){const n=ht();return n.withScope((()=>(n.getStackTop().scope=t,e(t))))}function gt(t){return ht().withScope((()=>t(ht().getIsolationScope())))}function yt(t){const e=tt(t);return e.acs?e.acs:{withIsolationScope:gt,withScope:mt,withSetScope:_t,withSetIsolationScope:(t,e)=>gt(e),getCurrentScope:()=>ht().getScope(),getIsolationScope:()=>ht().getIsolationScope()}}function vt(){return yt(Z()).getCurrentScope()}function Et(){return yt(Z()).getIsolationScope()}function bt(){return vt().getClient()}let St;const xt=new WeakMap,wt=()=>({name:"FunctionToString",setupOnce(){St=Function.prototype.toString;try{Function.prototype.toString=function(...t){const e=P(this),n=xt.has(bt())&&void 0!==e?e:this;return St.apply(n,t)}}catch(t){}},setup(t){xt.set(t,!0)}}),kt=50,$t="?",Ot=/\(error: (.*)\)/,Tt=/captureMessage|captureException/;function Dt(...t){const e=t.sort(((t,e)=>t[0]-e[0])).map((t=>t[1]));return(t,n=0,r=0)=>{const o=[],s=t.split("\n");for(let t=n;t1024)continue;const i=Ot.test(n)?n.replace(Ot,"$1"):n;if(!i.match(/\S*Error: /)){for(const t of e){const e=t(i);if(e){o.push(e);break}}if(o.length>=kt+r)break}}return function(t){if(!t.length)return[];const e=Array.from(t);/sentryWrapped/.test(Nt(e).function||"")&&e.pop();e.reverse(),Tt.test(Nt(e).function||"")&&(e.pop(),Tt.test(Nt(e).function||"")&&e.pop());return e.slice(0,kt).map((t=>({...t,filename:t.filename||Nt(e).filename,function:t.function||$t})))}(o.slice(r))}}function Nt(t){return t[t.length-1]||{}}const It="";function Pt(t){try{return t&&"function"==typeof t&&t.name||It}catch(t){return It}}function Rt(t){const e=t.exception;if(e){const t=[];try{return e.values.forEach((e=>{e.stacktrace.frames&&t.push(...e.stacktrace.frames)})),t}catch(t){return}}}const Ct=()=>{let t;return{name:"Dedupe",processEvent(e){if(e.type)return e;try{if(function(t,e){if(!e)return!1;if(function(t,e){const n=t.message,r=e.message;if(!n&&!r)return!1;if(n&&!r||!n&&r)return!1;if(n!==r)return!1;if(!At(t,e))return!1;if(!jt(t,e))return!1;return!0}(t,e))return!0;if(function(t,e){const n=Lt(e),r=Lt(t);if(!n||!r)return!1;if(n.type!==r.type||n.value!==r.value)return!1;if(!At(t,e))return!1;if(!jt(t,e))return!1;return!0}(t,e))return!0;return!1}(e,t))return G&&a.warn("Event dropped due to being a duplicate of previously captured event."),null}catch(t){}return t=e}}};function jt(t,e){let n=Rt(t),r=Rt(e);if(!n&&!r)return!0;if(n&&!r||!n&&r)return!1;if(r.length!==n.length)return!1;for(let t=0;t{console.warn("[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.")})));vt().update(e.initialScope);const n=new t(e);return function(t){vt().setClient(t)}(n),n.init(),n}const Ut="production";function qt(t,e=100,n=1/0){try{return Yt("",t,e,n)}catch(t){return{ERROR:`**non-serializable** (${t})`}}}function Ft(t,e=3,n=102400){const r=qt(t,e);return o=r,function(t){return~-encodeURI(t).split(/%..|./).length}(JSON.stringify(o))>n?Ft(t,e-1,n):r;var o}function Yt(t,e,n=1/0,r=1/0,o=function(){const t="function"==typeof WeakSet,e=t?new WeakSet:[];return[function(n){if(t)return!!e.has(n)||(e.add(n),!1);for(let t=0;t=r){l[t]="[MaxProperties ~]";break}const e=d[t];l[t]=Yt(t,e,c-1,r,o),p++}return i(e),l}var Bt;function Ht(t){return new Wt((e=>{e(t)}))}function Gt(t){return new Wt(((e,n)=>{n(t)}))}!function(t){t[t.PENDING=0]="PENDING";t[t.RESOLVED=1]="RESOLVED";t[t.REJECTED=2]="REJECTED"}(Bt||(Bt={}));class Wt{constructor(t){Wt.prototype.__init.call(this),Wt.prototype.__init2.call(this),Wt.prototype.__init3.call(this),Wt.prototype.__init4.call(this),this._state=Bt.PENDING,this._handlers=[];try{t(this._resolve,this._reject)}catch(t){this._reject(t)}}then(t,e){return new Wt(((n,r)=>{this._handlers.push([!1,e=>{if(t)try{n(t(e))}catch(t){r(t)}else n(e)},t=>{if(e)try{n(e(t))}catch(t){r(t)}else r(t)}]),this._executeHandlers()}))}catch(t){return this.then((t=>t),t)}finally(t){return new Wt(((e,n)=>{let r,o;return this.then((e=>{o=!1,r=e,t&&t()}),(e=>{o=!0,r=e,t&&t()})).then((()=>{o?n(r):e(r)}))}))}__init(){this._resolve=t=>{this._setResult(Bt.RESOLVED,t)}}__init2(){this._reject=t=>{this._setResult(Bt.REJECTED,t)}}__init3(){this._setResult=(t,e)=>{this._state===Bt.PENDING&&(y(e)?e.then(this._resolve,this._reject):(this._state=t,this._value=e,this._executeHandlers()))}}__init4(){this._executeHandlers=()=>{if(this._state===Bt.PENDING)return;const t=this._handlers.slice();this._handlers=[],t.forEach((t=>{t[0]||(this._state===Bt.RESOLVED&&t[1](this._value),this._state===Bt.REJECTED&&t[2](this._value),t[0]=!0)}))}}}function zt(t,e,n,r=0){return new Wt(((o,s)=>{const i=t[r];if(null===e||"function"!=typeof i)o(e);else{const c=i({...e},n);G&&i.id&&null===c&&a.log(`Event processor "${i.id}" dropped event`),y(c)?c.then((e=>zt(t,e,n,r+1).then(o))).then(null,s):zt(t,c,n,r+1).then(o).then(null,s)}}))}const Jt="sentry-",Vt=/^sentry-/;function Kt(t){const e=function(t){if(!t||!f(t)&&!Array.isArray(t))return;if(Array.isArray(t))return t.reduce(((t,e)=>{const n=Xt(e);return Object.entries(n).forEach((([e,n])=>{t[e]=n})),t}),{});return Xt(t)}(t);if(!e)return;const n=Object.entries(e).reduce(((t,[e,n])=>{if(e.match(Vt)){t[e.slice(Jt.length)]=n}return t}),{});return Object.keys(n).length>0?n:void 0}function Xt(t){return t.split(",").map((t=>t.split("=").map((t=>decodeURIComponent(t.trim()))))).reduce(((t,[e,n])=>(e&&n&&(t[e]=n),t)),{})}const Qt="sentry.source",Zt="sentry.sample_rate",te="sentry.op",ee="sentry.origin";const ne="_sentryMetrics";function re(t){const e=t[ne];if(!e)return;const n={};for(const[,[t,r]]of e){(n[t]||(n[t]=[])).push(A(r))}return n}const oe=0,se=1;const ie=1;function ae(t){const{spanId:e,traceId:n}=t.spanContext(),{parent_span_id:r}=le(t);return A({parent_span_id:r,span_id:e,trace_id:n})}function ce(t){return"number"==typeof t?ue(t):Array.isArray(t)?t[0]+t[1]/1e9:t instanceof Date?ue(t.getTime()):ot()}function ue(t){return t>9999999999?t/1e3:t}function le(t){if(function(t){return"function"==typeof t.getSpanJSON}(t))return t.getSpanJSON();try{const{spanId:e,traceId:n}=t.spanContext();if(function(t){const e=t;return!!(e.attributes&&e.startTime&&e.name&&e.endTime&&e.status)}(t)){const{attributes:r,startTime:o,name:s,endTime:i,parentSpanId:a,status:c}=t;return A({span_id:e,trace_id:n,data:r,description:s,parent_span_id:a,start_timestamp:ce(o),timestamp:ce(i)||void 0,status:de(c),op:r[te],origin:r[ee],_metrics_summary:re(t)})}return{span_id:e,trace_id:n}}catch(t){return{}}}function pe(t){const{traceFlags:e}=t.spanContext();return e===ie}function de(t){if(t&&t.code!==oe)return t.code===se?"ok":t.message||"unknown_error"}const fe="_sentryRootSpan";function he(t){return t[fe]||t}const me="_frozenDsc";function _e(t,e){const n=e.getOptions(),{publicKey:r}=e.getDsn()||{},o=A({environment:n.environment||Ut,release:n.release,public_key:r,trace_id:t});return e.emit("createDsc",o),o}function ge(t){const e=bt();if(!e)return{};const n=_e(le(t).trace_id||"",e),r=he(t),o=r[me];if(o)return o;const s=r.spanContext().traceState,i=s&&s.get("sentry.dsc"),a=i&&Kt(i);if(a)return a;const c=le(r),u=c.data||{},l=u[Zt];null!=l&&(n.sample_rate=`${l}`);const p=u[Qt],d=c.description;return"url"!==p&&d&&(n.transaction=d),function(t){if("boolean"==typeof __SENTRY_TRACING__&&!__SENTRY_TRACING__)return!1;const e=bt(),n=t||e&&e.getOptions();return!!n&&(n.enableTracing||"tracesSampleRate"in n||"tracesSampler"in n)}()&&(n.sampled=String(pe(r))),e.emit("createDsc",n,r),n}function ye(t,e){const{fingerprint:n,span:r,breadcrumbs:o,sdkProcessingMetadata:s}=e;!function(t,e){const{extra:n,tags:r,user:o,contexts:s,level:i,transactionName:a}=e,c=A(n);c&&Object.keys(c).length&&(t.extra={...c,...t.extra});const u=A(r);u&&Object.keys(u).length&&(t.tags={...u,...t.tags});const l=A(o);l&&Object.keys(l).length&&(t.user={...l,...t.user});const p=A(s);p&&Object.keys(p).length&&(t.contexts={...p,...t.contexts});i&&(t.level=i);a&&"transaction"!==t.type&&(t.transaction=a)}(t,e),r&&function(t,e){t.contexts={trace:ae(e),...t.contexts},t.sdkProcessingMetadata={dynamicSamplingContext:ge(e),...t.sdkProcessingMetadata};const n=he(e),r=le(n).description;r&&!t.transaction&&"transaction"===t.type&&(t.transaction=r)}(t,r),function(t,e){t.fingerprint=t.fingerprint?H(t.fingerprint):[],e&&(t.fingerprint=t.fingerprint.concat(e));t.fingerprint&&!t.fingerprint.length&&delete t.fingerprint}(t,n),function(t,e){const n=[...t.breadcrumbs||[],...e];t.breadcrumbs=n.length?n:void 0}(t,o),function(t,e){t.sdkProcessingMetadata={...t.sdkProcessingMetadata,...e}}(t,s)}function ve(t,e){const{extra:n,tags:r,user:o,contexts:s,level:i,sdkProcessingMetadata:a,breadcrumbs:c,fingerprint:u,eventProcessors:l,attachments:p,propagationContext:d,transactionName:f,span:h}=e;Ee(t,"extra",n),Ee(t,"tags",r),Ee(t,"user",o),Ee(t,"contexts",s),Ee(t,"sdkProcessingMetadata",a),i&&(t.level=i),f&&(t.transactionName=f),h&&(t.span=h),c.length&&(t.breadcrumbs=[...t.breadcrumbs,...c]),u.length&&(t.fingerprint=[...t.fingerprint,...u]),l.length&&(t.eventProcessors=[...t.eventProcessors,...l]),p.length&&(t.attachments=[...t.attachments,...p]),t.propagationContext={...t.propagationContext,...d}}function Ee(t,e,n){if(n&&Object.keys(n).length){t[e]={...t[e]};for(const r in n)Object.prototype.hasOwnProperty.call(n,r)&&(t[e][r]=n[r])}}function be(t,e,o,s,i,a){const{normalizeDepth:c=3,normalizeMaxBreadth:u=1e3}=t,l={...e,event_id:e.event_id||o.event_id||M(),timestamp:e.timestamp||rt()},p=o.integrations||t.integrations.map((t=>t.name));!function(t,e){const{environment:n,release:r,dist:o,maxValueLength:s=250}=e;"environment"in t||(t.environment="environment"in e?n:Ut);void 0===t.release&&void 0!==r&&(t.release=r);void 0===t.dist&&void 0!==o&&(t.dist=o);t.message&&(t.message=k(t.message,s));const i=t.exception&&t.exception.values&&t.exception.values[0];i&&i.value&&(i.value=k(i.value,s));const a=t.request;a&&a.url&&(a.url=k(a.url,s))}(l,t),function(t,e){e.length>0&&(t.sdk=t.sdk||{},t.sdk.integrations=[...t.sdk.integrations||[],...e])}(l,p),i&&i.emit("applyFrameMetadata",e),void 0===e.type&&function(t,e){const r=n._sentryDebugIds;if(!r)return;let o;const s=Se.get(e);s?o=s:(o=new Map,Se.set(e,o));const i=Object.entries(r).reduce(((t,[n,r])=>{let s;const i=o.get(n);i?s=i:(s=e(n),o.set(n,s));for(let e=s.length-1;e>=0;e--){const n=s[e];if(n.filename){t[n.filename]=r;break}}return t}),{});try{t.exception.values.forEach((t=>{t.stacktrace.frames.forEach((t=>{t.filename&&(t.debug_id=i[t.filename])}))}))}catch(t){}}(l,t.stackParser);const d=function(t,e){if(!e)return t;const n=t?t.clone():new dt;return n.update(e),n}(s,o.captureContext);o.mechanism&&Y(l,o.mechanism);const f=i?i.getEventProcessors():[],h=r("globalScope",(()=>new dt)).getScopeData();if(a){ve(h,a.getScopeData())}if(d){ve(h,d.getScopeData())}const m=[...o.attachments||[],...h.attachments];m.length&&(o.attachments=m),ye(l,h);return zt([...f,...h.eventProcessors],l,o).then((t=>(t&&function(t){const e={};try{t.exception.values.forEach((t=>{t.stacktrace.frames.forEach((t=>{t.debug_id&&(t.abs_path?e[t.abs_path]=t.debug_id:t.filename&&(e[t.filename]=t.debug_id),delete t.debug_id)}))}))}catch(t){}if(0===Object.keys(e).length)return;t.debug_meta=t.debug_meta||{},t.debug_meta.images=t.debug_meta.images||[];const n=t.debug_meta.images;Object.entries(e).forEach((([t,e])=>{n.push({type:"sourcemap",code_file:t,debug_id:e})}))}(t),"number"==typeof c&&c>0?function(t,e,n){if(!t)return null;const r={...t,...t.breadcrumbs&&{breadcrumbs:t.breadcrumbs.map((t=>({...t,...t.data&&{data:qt(t.data,e,n)}})))},...t.user&&{user:qt(t.user,e,n)},...t.contexts&&{contexts:qt(t.contexts,e,n)},...t.extra&&{extra:qt(t.extra,e,n)}};t.contexts&&t.contexts.trace&&r.contexts&&(r.contexts.trace=t.contexts.trace,t.contexts.trace.data&&(r.contexts.trace.data=qt(t.contexts.trace.data,e,n)));t.spans&&(r.spans=t.spans.map((t=>({...t,...t.data&&{data:qt(t.data,e,n)}}))));return r}(t,c,u):t)))}const Se=new WeakMap;function xe(t){if(t)return function(t){return t instanceof dt||"function"==typeof t}(t)||function(t){return Object.keys(t).some((t=>we.includes(t)))}(t)?{captureContext:t}:t}const we=["user","level","extra","contexts","tags","fingerprint","requestSession","propagationContext"];function ke(t,e){return vt().captureEvent(t,e)}function $e(t){const e=bt(),r=Et(),o=vt(),{release:s,environment:i=Ut}=e&&e.getOptions()||{},{userAgent:a}=n.navigator||{},c=it({release:s,environment:i,user:o.getUser()||r.getUser(),...a&&{userAgent:a},...t}),u=r.getSession();return u&&"ok"===u.status&&at(u,{status:"exited"}),Oe(),r.setSession(c),o.setSession(c),c}function Oe(){const t=Et(),e=vt(),n=e.getSession()||t.getSession();n&&function(t,e){let n={};e?n={status:e}:"ok"===t.status&&(n={status:"exited"}),at(t,n)}(n),Te(),t.setSession(),e.setSession()}function Te(){const t=Et(),e=vt(),n=bt(),r=e.getSession()||t.getSession();r&&n&&n.captureSession(r)}function De(t=!1){t?Oe():Te()}const Ne=/^(?:(\w+):)\/\/(?:(\w+)(?::(\w+)?)?@)([\w.-]+)(?::(\d+))?\/(.+)/;function Ie(t,e=!1){const{host:n,path:r,pass:o,port:s,projectId:i,protocol:a,publicKey:c}=t;return`${a}://${c}${e&&o?`:${o}`:""}@${n}${s?`:${s}`:""}/${r?`${r}/`:r}${i}`}function Pe(t){return{protocol:t.protocol,publicKey:t.publicKey||"",pass:t.pass||"",host:t.host,port:t.port||"",path:t.path||"",projectId:t.projectId}}function Re(e){const n="string"==typeof e?function(t){const e=Ne.exec(t);if(!e)return void i((()=>{console.error(`Invalid Sentry Dsn: ${t}`)}));const[n,r,o="",s="",a="",c=""]=e.slice(1);let u="",l=c;const p=l.split("/");if(p.length>1&&(u=p.slice(0,-1).join("/"),l=p.pop()),l){const t=l.match(/^\d+/);t&&(l=t[0])}return Pe({host:s,pass:o,path:u,projectId:l,port:a,protocol:n,publicKey:r})}(e):Pe(e);if(n&&function(e){if(!t)return!0;const{port:n,projectId:r,protocol:o}=e;return!(["protocol","publicKey","host","projectId"].find((t=>!e[t]&&(a.error(`Invalid Sentry Dsn: ${t} missing`),!0)))||(r.match(/^\d+$/)?function(t){return"http"===t||"https"===t}(o)?n&&isNaN(parseInt(n,10))&&(a.error(`Invalid Sentry Dsn: Invalid port ${n}`),1):(a.error(`Invalid Sentry Dsn: Invalid protocol ${o}`),1):(a.error(`Invalid Sentry Dsn: Invalid projectId ${r}`),1)))}(n))return n}const Ce="7";function je(t){const e=t.protocol?`${t.protocol}:`:"",n=t.port?`:${t.port}`:"";return`${e}//${t.host}${n}${t.path?`/${t.path}`:""}/api/`}function Ae(t,e){return n={sentry_key:t.publicKey,sentry_version:Ce,...e&&{sentry_client:`${e.name}/${e.version}`}},Object.keys(n).map((t=>`${encodeURIComponent(t)}=${encodeURIComponent(n[t])}`)).join("&");var n}function Le(t,e,n){return e||`${function(t){return`${je(t)}${t.projectId}/envelope/`}(t)}?${Ae(t,n)}`}const Me=n;function Ue(){if(!("fetch"in Me))return!1;try{return new Headers,new Request("http://www.example.com"),new Response,!0}catch(t){return!1}}function qe(t){return t&&/^function\s+\w+\(\)\s+\{\s+\[native code\]\s+\}$/.test(t.toString())}const Fe={},Ye={};function Be(t,e){Fe[t]=Fe[t]||[],Fe[t].push(e)}function He(t,e){Ye[t]||(e(),Ye[t]=!0)}function Ge(e,n){const r=e&&Fe[e];if(r)for(const o of r)try{o(n)}catch(n){t&&a.error(`Error while triggering instrumentation handler.\nType: ${e}\nName: ${Pt(o)}\nError:`,n)}}const We=n;const ze=n;let Je;function Ve(t){const e="history";Be(e,t),He(e,Ke)}function Ke(){if(!function(){const t=We.chrome,e=t&&t.app&&t.app.runtime,n="history"in We&&!!We.history.pushState&&!!We.history.replaceState;return!e&&n}())return;const t=ze.onpopstate;function e(t){return function(...e){const n=e.length>2?e[2]:void 0;if(n){const t=Je,e=String(n);Je=e;Ge("history",{from:t,to:e})}return t.apply(this,e)}}ze.onpopstate=function(...e){const n=ze.location.href,r=Je;Je=n;if(Ge("history",{from:r,to:n}),t)try{return t.apply(this,e)}catch(t){}},D(ze.history,"pushState",e),D(ze.history,"replaceState",e)}function Xe(t,e=[]){return[t,e]}function Qe(t,e){const[n,r]=t;return[n,[...r,e]]}function Ze(t,e){const n=t[1];for(const t of n){if(e(t,t[0].type))return!0}return!1}function tn(t){return n.__SENTRY__&&n.__SENTRY__.encodePolyfill?n.__SENTRY__.encodePolyfill(t):(new TextEncoder).encode(t)}function en(t){const[e,n]=t;let r=JSON.stringify(e);function o(t){"string"==typeof r?r="string"==typeof t?r+t:[tn(r),t]:r.push("string"==typeof t?tn(t):t)}for(const t of n){const[e,n]=t;if(o(`\n${JSON.stringify(e)}\n`),"string"==typeof n||n instanceof Uint8Array)o(n);else{let t;try{t=JSON.stringify(n)}catch(e){t=JSON.stringify(qt(n))}o(t)}}return"string"==typeof r?r:function(t){const e=t.reduce(((t,e)=>t+e.length),0),n=new Uint8Array(e);let r=0;for(const e of t)n.set(e,r),r+=e.length;return n}(r)}function nn(t){const e="string"==typeof t.data?tn(t.data):t.data;return[A({type:"attachment",length:e.length,filename:t.filename,content_type:t.contentType,attachment_type:t.attachmentType}),e]}const rn={session:"session",sessions:"session",attachment:"attachment",transaction:"transaction",event:"error",client_report:"internal",user_report:"default",profile:"profile",profile_chunk:"profile",replay_event:"replay",replay_recording:"replay",check_in:"monitor",feedback:"feedback",span:"span",statsd:"metric_bucket"};function on(t){return rn[t]}function sn(t){if(!t||!t.sdk)return;const{name:e,version:n}=t.sdk;return{name:e,version:n}}class an extends Error{constructor(t,e="warn"){super(t),this.message=t,this.name=new.target.prototype.constructor.name,Object.setPrototypeOf(this,new.target.prototype),this.logLevel=e}}function cn(t,e,n,r){const o=sn(n),s=t.type&&"replay_event"!==t.type?t.type:"event";!function(t,e){e&&(t.sdk=t.sdk||{},t.sdk.name=t.sdk.name||e.name,t.sdk.version=t.sdk.version||e.version,t.sdk.integrations=[...t.sdk.integrations||[],...e.integrations||[]],t.sdk.packages=[...t.sdk.packages||[],...e.packages||[]])}(t,n&&n.sdk);const i=function(t,e,n,r){const o=t.sdkProcessingMetadata&&t.sdkProcessingMetadata.dynamicSamplingContext;return{event_id:t.event_id,sent_at:(new Date).toISOString(),...e&&{sdk:e},...!!n&&r&&{dsn:Ie(r)},...o&&{trace:A({...o})}}}(t,o,r,e);delete t.sdkProcessingMetadata;return Xe(i,[[{type:s},t]])}const un="Not capturing exception because it's already been captured.";class ln{constructor(t){if(this._options=t,this._integrations={},this._numProcessing=0,this._outcomes={},this._hooks={},this._eventProcessors=[],t.dsn?this._dsn=Re(t.dsn):G&&a.warn("No DSN provided, client will not send events."),this._dsn){const e=Le(this._dsn,t.tunnel,t._metadata?t._metadata.sdk:void 0);this._transport=t.transport({tunnel:this._options.tunnel,recordDroppedEvent:this.recordDroppedEvent.bind(this),...t.transportOptions,url:e})}}captureException(t,e,n){const r=M();if(B(t))return G&&a.log(un),r;const o={event_id:r,...e};return this._process(this.eventFromException(t,o).then((t=>this._captureEvent(t,o,n)))),o.event_id}captureMessage(t,e,n,r){const o={event_id:M(),...n},s=h(t)?t:String(t),i=m(t)?this.eventFromMessage(s,e,o):this.eventFromException(t,o);return this._process(i.then((t=>this._captureEvent(t,o,r)))),o.event_id}captureEvent(t,e,n){const r=M();if(e&&e.originalException&&B(e.originalException))return G&&a.log(un),r;const o={event_id:r,...e},s=(t.sdkProcessingMetadata||{}).capturedSpanScope;return this._process(this._captureEvent(t,o,s||n)),o.event_id}captureSession(t){"string"!=typeof t.release?G&&a.warn("Discarded session because of missing or non-string release"):(this.sendSession(t),at(t,{init:!1}))}getDsn(){return this._dsn}getOptions(){return this._options}getSdkMetadata(){return this._options._metadata}getTransport(){return this._transport}flush(t){const e=this._transport;return e?(this.emit("flush"),this._isClientDoneProcessing(t).then((n=>e.flush(t).then((t=>n&&t))))):Ht(!0)}close(t){return this.flush(t).then((t=>(this.getOptions().enabled=!1,this.emit("close"),t)))}getEventProcessors(){return this._eventProcessors}addEventProcessor(t){this._eventProcessors.push(t)}init(){(this._isEnabled()||this._options.integrations.some((({name:t})=>t.startsWith("Spotlight"))))&&this._setupIntegrations()}getIntegrationByName(t){return this._integrations[t]}addIntegration(t){const e=this._integrations[t.name];V(this,t,this._integrations),e||J(this,[t])}sendEvent(t,e={}){this.emit("beforeSendEvent",t,e);let n=cn(t,this._dsn,this._options._metadata,this._options.tunnel);for(const t of e.attachments||[])n=Qe(n,nn(t));const r=this.sendEnvelope(n);r&&r.then((e=>this.emit("afterSendEvent",t,e)),null)}sendSession(t){const e=function(t,e,n,r){const o=sn(n);return Xe({sent_at:(new Date).toISOString(),...o&&{sdk:o},...!!r&&e&&{dsn:Ie(e)}},["aggregates"in t?[{type:"sessions"},t]:[{type:"session"},t.toJSON()]])}(t,this._dsn,this._options._metadata,this._options.tunnel);this.sendEnvelope(e)}recordDroppedEvent(t,e,n){if(this._options.sendClientReports){const r="number"==typeof n?n:1,o=`${t}:${e}`;G&&a.log(`Recording outcome: "${o}"${r>1?` (${r} times)`:""}`),this._outcomes[o]=(this._outcomes[o]||0)+r}}on(t,e){const n=this._hooks[t]=this._hooks[t]||[];return n.push(e),()=>{const t=n.indexOf(e);t>-1&&n.splice(t,1)}}emit(t,...e){const n=this._hooks[t];n&&n.forEach((t=>t(...e)))}sendEnvelope(t){return this.emit("beforeEnvelope",t),this._isEnabled()&&this._transport?this._transport.send(t).then(null,(t=>(G&&a.error("Error while sending event:",t),t))):(G&&a.error("Transport disabled"),Ht({}))}_setupIntegrations(){const{integrations:t}=this._options;this._integrations=function(t,e){const n={};return e.forEach((e=>{e&&V(t,e,n)})),n}(this,t),J(this,t)}_updateSessionFromEvent(t,e){let n=!1,r=!1;const o=e.exception&&e.exception.values;if(o){r=!0;for(const t of o){const e=t.mechanism;if(e&&!1===e.handled){n=!0;break}}}const s="ok"===t.status;(s&&0===t.errors||s&&n)&&(at(t,{...n&&{status:"crashed"},errors:t.errors||Number(r||n)}),this.captureSession(t))}_isClientDoneProcessing(t){return new Wt((e=>{let n=0;const r=setInterval((()=>{0==this._numProcessing?(clearInterval(r),e(!0)):(n+=1,t&&n>=t&&(clearInterval(r),e(!1)))}),1)}))}_isEnabled(){return!1!==this.getOptions().enabled&&void 0!==this._transport}_prepareEvent(t,e,n,r=Et()){const o=this.getOptions(),s=Object.keys(this._integrations);return!e.integrations&&s.length>0&&(e.integrations=s),this.emit("preprocessEvent",t,e),t.type||r.setLastEventId(t.event_id||e.event_id),be(o,t,e,n,this,r).then((t=>{if(null===t)return t;const e={...r.getPropagationContext(),...n?n.getPropagationContext():void 0};if(!(t.contexts&&t.contexts.trace)&&e){const{traceId:n,spanId:r,parentSpanId:o,dsc:s}=e;t.contexts={trace:A({trace_id:n,span_id:r,parent_span_id:o}),...t.contexts};const i=s||_e(n,this);t.sdkProcessingMetadata={dynamicSamplingContext:i,...t.sdkProcessingMetadata}}return t}))}_captureEvent(t,e={},n){return this._processEvent(t,e,n).then((t=>t.event_id),(t=>{if(G){const e=t;"log"===e.logLevel?a.log(e.message):a.warn(e)}}))}_processEvent(t,e,n){const r=this.getOptions(),{sampleRate:o}=r,s=dn(t),i=pn(t),c=t.type||"error",u=`before send for type \`${c}\``,l=void 0===o?void 0:function(t){if("boolean"==typeof t)return Number(t);const e="string"==typeof t?parseFloat(t):t;if(!("number"!=typeof e||isNaN(e)||e<0||e>1))return e;G&&a.warn(`[Tracing] Given sample rate is invalid. Sample rate must be a boolean or a number between 0 and 1. Got ${JSON.stringify(t)} of type ${JSON.stringify(typeof t)}.`)}(o);if(i&&"number"==typeof l&&Math.random()>l)return this.recordDroppedEvent("sample_rate","error",t),Gt(new an(`Discarding event because it's not included in the random sample (sampling rate = ${o})`,"log"));const p="replay_event"===c?"replay":c,d=(t.sdkProcessingMetadata||{}).capturedSpanIsolationScope;return this._prepareEvent(t,e,n,d).then((n=>{if(null===n)throw this.recordDroppedEvent("event_processor",p,t),new an("An event processor returned `null`, will not send event.","log");if(e.data&&!0===e.data.__sentry__)return n;const o=function(t,e,n,r){const{beforeSend:o,beforeSendTransaction:s,beforeSendSpan:i}=e;if(pn(n)&&o)return o(n,r);if(dn(n)){if(n.spans&&i){const e=[];for(const r of n.spans){const n=i(r);n?e.push(n):t.recordDroppedEvent("before_send","span")}n.spans=e}if(s){if(n.spans){const t=n.spans.length;n.sdkProcessingMetadata={...n.sdkProcessingMetadata,spanCountBeforeProcessing:t}}return s(n,r)}}return n}(this,r,n,e);return function(t,e){const n=`${e} must return \`null\` or a valid event.`;if(y(t))return t.then((t=>{if(!_(t)&&null!==t)throw new an(n);return t}),(t=>{throw new an(`${e} rejected with ${t}`)}));if(!_(t)&&null!==t)throw new an(n);return t}(o,u)})).then((r=>{if(null===r){if(this.recordDroppedEvent("before_send",p,t),s){const e=1+(t.spans||[]).length;this.recordDroppedEvent("before_send","span",e)}throw new an(`${u} returned \`null\`, will not send event.`,"log")}const o=n&&n.getSession();if(!s&&o&&this._updateSessionFromEvent(o,r),s){const t=(r.sdkProcessingMetadata&&r.sdkProcessingMetadata.spanCountBeforeProcessing||0)-(r.spans?r.spans.length:0);t>0&&this.recordDroppedEvent("before_send","span",t)}const i=r.transaction_info;if(s&&i&&r.transaction!==t.transaction){const t="custom";r.transaction_info={...i,source:t}}return this.sendEvent(r,e),r})).then(null,(t=>{if(t instanceof an)throw t;throw this.captureException(t,{data:{__sentry__:!0},originalException:t}),new an(`Event processing pipeline threw an error, original event will not be sent. Details have been sent as a new event.\nReason: ${t}`)}))}_process(t){this._numProcessing++,t.then((t=>(this._numProcessing--,t)),(t=>(this._numProcessing--,t)))}_clearOutcomes(){const t=this._outcomes;return this._outcomes={},Object.entries(t).map((([t,e])=>{const[n,r]=t.split(":");return{reason:n,category:r,quantity:e}}))}_flushOutcomes(){G&&a.log("Flushing outcomes...");const t=this._clearOutcomes();if(0===t.length)return void(G&&a.log("No outcomes to send"));if(!this._dsn)return void(G&&a.log("No dsn provided, will not send outcomes"));G&&a.log("Sending outcomes:",t);const e=(n=t,Xe((r=this._options.tunnel&&Ie(this._dsn))?{dsn:r}:{},[[{type:"client_report"},{timestamp:o||rt(),discarded_events:n}]]));var n,r,o;this.sendEnvelope(e)}}function pn(t){return void 0===t.type}function dn(t){return"transaction"===t.type}const fn="undefined"==typeof __SENTRY_DEBUG__||__SENTRY_DEBUG__;function hn(t,e){const n=gn(t,e),r={type:e&&e.name,value:vn(e)};return n.length&&(r.stacktrace={frames:n}),void 0===r.type&&""===r.value&&(r.value="Unrecoverable error caught"),r}function mn(t,e,n,r){const o=bt(),s=o&&o.getOptions().normalizeDepth,i=function(t){for(const e in t)if(Object.prototype.hasOwnProperty.call(t,e)){const n=t[e];if(n instanceof Error)return n}return}(e),a={__serialized__:Ft(e,s)};if(i)return{exception:{values:[hn(t,i)]},extra:a};const c={exception:{values:[{type:g(e)?e.constructor.name:r?"UnhandledRejection":"Error",value:Sn(e,{isUnhandledRejection:r})}]},extra:a};if(n){const e=gn(t,n);e.length&&(c.exception.values[0].stacktrace={frames:e})}return c}function _n(t,e){return{exception:{values:[hn(t,e)]}}}function gn(t,e){const n=e.stacktrace||e.stack||"",r=function(t){if(t&&yn.test(t.message))return 1;return 0}(e),o=function(t){if("number"==typeof t.framesToPop)return t.framesToPop;return 0}(e);try{return t(n,r,o)}catch(t){}return[]}const yn=/Minified React error #\d+;/i;function vn(t){const e=t&&t.message;return e?e.error&&"string"==typeof e.error.message?e.error.message:e:"No error message"}function En(t,e,n,r,o){let s;if(p(e)&&e.error){return _n(t,e.error)}if(d(e)||l(e,"DOMException")){const o=e;if("stack"in e)s=_n(t,e);else{const e=o.name||(d(o)?"DOMError":"DOMException"),i=o.message?`${e}: ${o.message}`:e;s=bn(t,i,n,r),F(s,i)}return"code"in o&&(s.tags={...s.tags,"DOMException.code":`${o.code}`}),s}if(u(e))return _n(t,e);if(_(e)||g(e)){return s=mn(t,e,n,o),Y(s,{synthetic:!0}),s}return s=bn(t,e,n,r),F(s,`${e}`,void 0),Y(s,{synthetic:!0}),s}function bn(t,e,n,r){const o={};if(r&&n){const r=gn(t,n);r.length&&(o.exception={values:[{value:e,stacktrace:{frames:r}}]})}if(h(e)){const{__sentry_template_string__:t,__sentry_template_values__:n}=e;return o.logentry={message:t,params:n},o}return o.message=e,o}function Sn(t,{isUnhandledRejection:e}){const n=function(t,e=40){const n=Object.keys(R(t));n.sort();const r=n[0];if(!r)return"[object has no keys]";if(r.length>=e)return k(r,e);for(let t=n.length;t>0;t--){const r=n.slice(0,t).join(", ");if(!(r.length>e))return t===n.length?r:k(r,e)}return""}(t),r=e?"promise rejection":"exception";if(p(t))return`Event \`ErrorEvent\` captured as ${r} with message \`${t.message}\``;if(g(t)){return`Event \`${function(t){try{const e=Object.getPrototypeOf(t);return e?e.constructor.name:void 0}catch(t){}}(t)}\` (type=${t.type}) captured as ${r}`}return`Object captured as ${r} with keys: ${n}`}const xn=n;let wn=0;function kn(){return wn>0}function $n(t,e={},n){if("function"!=typeof t)return t;try{const e=t.__sentry_wrapped__;if(e)return"function"==typeof e?e:t;if(P(t))return t}catch(e){return t}const r=function(){const r=Array.prototype.slice.call(arguments);try{n&&"function"==typeof n&&n.apply(this,arguments);const o=r.map((t=>$n(t,e)));return t.apply(this,o)}catch(t){throw wn++,setTimeout((()=>{wn--})),function(...t){const e=yt(Z());if(2===t.length){const[n,r]=t;return n?e.withSetScope(n,r):e.withScope(r)}e.withScope(t[0])}((n=>{var o,s;n.addEventProcessor((t=>(e.mechanism&&(F(t,void 0,void 0),Y(t,e.mechanism)),t.extra={...t.extra,arguments:r},t))),o=t,vt().captureException(o,xe(s))})),t}};try{for(const e in t)Object.prototype.hasOwnProperty.call(t,e)&&(r[e]=t[e])}catch(t){}I(r,t),N(t,"__sentry_wrapped__",r);try{Object.getOwnPropertyDescriptor(r,"name").configurable&&Object.defineProperty(r,"name",{get:()=>t.name})}catch(t){}return r}class On extends ln{constructor(t){const n={parentSpanIsAlwaysRootSpan:!0,...t};!function(t,n,r=[n],o="npm"){const s=t._metadata||{};s.sdk||(s.sdk={name:`sentry.javascript.${n}`,packages:r.map((t=>({name:`${o}:@sentry/${t}`,version:e}))),version:e}),t._metadata=s}(n,"browser",["browser"],xn.SENTRY_SDK_SOURCE||"npm"),super(n),n.sendClientReports&&xn.document&&xn.document.addEventListener("visibilitychange",(()=>{"hidden"===xn.document.visibilityState&&this._flushOutcomes()}))}eventFromException(t,e){return function(t,e,n,r){const o=En(t,e,n&&n.syntheticException||void 0,r);return Y(o),o.level="error",n&&n.event_id&&(o.event_id=n.event_id),Ht(o)}(this._options.stackParser,t,e,this._options.attachStacktrace)}eventFromMessage(t,e="info",n){return function(t,e,n="info",r,o){const s=bn(t,e,r&&r.syntheticException||void 0,o);return s.level=n,r&&r.event_id&&(s.event_id=r.event_id),Ht(s)}(this._options.stackParser,t,e,n,this._options.attachStacktrace)}captureUserFeedback(t){if(!this._isEnabled())return void(fn&&a.warn("SDK not enabled, will not capture user feedback."));const e=function(t,{metadata:e,tunnel:n,dsn:r}){const o={event_id:t.event_id,sent_at:(new Date).toISOString(),...e&&e.sdk&&{sdk:{name:e.sdk.name,version:e.sdk.version}},...!!n&&!!r&&{dsn:Ie(r)}},s=function(t){return[{type:"user_report"},t]}(t);return Xe(o,[s])}(t,{metadata:this.getSdkMetadata(),dsn:this.getDsn(),tunnel:this.getOptions().tunnel});this.sendEnvelope(e)}_prepareEvent(t,e,n){return t.platform=t.platform||"javascript",super._prepareEvent(t,e,n)}}const Tn=1e3;let Dn,Nn,In;function Pn(){if(!ze.document)return;const t=Ge.bind(null,"dom"),e=Rn(t,!0);ze.document.addEventListener("click",e,!1),ze.document.addEventListener("keypress",e,!1),["EventTarget","Node"].forEach((e=>{const n=ze[e]&&ze[e].prototype;n&&n.hasOwnProperty&&n.hasOwnProperty("addEventListener")&&(D(n,"addEventListener",(function(e){return function(n,r,o){if("click"===n||"keypress"==n)try{const r=this,s=r.__sentry_instrumentation_handlers__=r.__sentry_instrumentation_handlers__||{},i=s[n]=s[n]||{refCount:0};if(!i.handler){const r=Rn(t);i.handler=r,e.call(this,n,r,o)}i.refCount++}catch(t){}return e.call(this,n,r,o)}})),D(n,"removeEventListener",(function(t){return function(e,n,r){if("click"===e||"keypress"==e)try{const n=this,o=n.__sentry_instrumentation_handlers__||{},s=o[e];s&&(s.refCount--,s.refCount<=0&&(t.call(this,e,s.handler,r),s.handler=void 0,delete o[e]),0===Object.keys(o).length&&delete n.__sentry_instrumentation_handlers__)}catch(t){}return t.call(this,e,n,r)}})))}))}function Rn(t,e=!1){return n=>{if(!n||n._sentryCaptured)return;const r=function(t){try{return t.target}catch(t){return null}}(n);if(function(t,e){return"keypress"===t&&(!e||!e.tagName||"INPUT"!==e.tagName&&"TEXTAREA"!==e.tagName&&!e.isContentEditable)}(n.type,r))return;N(n,"_sentryCaptured",!0),r&&!r._sentryId&&N(r,"_sentryId",M());const o="keypress"===n.type?"input":n.type;if(!function(t){if(t.type!==Nn)return!1;try{if(!t.target||t.target._sentryId!==In)return!1}catch(t){}return!0}(n)){t({event:n,name:o,global:e}),Nn=n.type,In=r?r._sentryId:void 0}clearTimeout(Dn),Dn=ze.setTimeout((()=>{In=void 0,Nn=void 0}),Tn)}}const Cn="__sentry_xhr_v3__";function jn(){if(!ze.XMLHttpRequest)return;const t=XMLHttpRequest.prototype;t.open=new Proxy(t.open,{apply(t,e,n){const r=1e3*ot(),o=f(n[0])?n[0].toUpperCase():void 0,s=function(t){if(f(t))return t;try{return t.toString()}catch(t){}return}(n[1]);if(!o||!s)return t.apply(e,n);e[Cn]={method:o,url:s,request_headers:{}},"POST"===o&&s.match(/sentry_key/)&&(e.__sentry_own_request__=!0);const i=()=>{const t=e[Cn];if(t&&4===e.readyState){try{t.status_code=e.status}catch(t){}Ge("xhr",{endTimestamp:1e3*ot(),startTimestamp:r,xhr:e})}};return"onreadystatechange"in e&&"function"==typeof e.onreadystatechange?e.onreadystatechange=new Proxy(e.onreadystatechange,{apply:(t,e,n)=>(i(),t.apply(e,n))}):e.addEventListener("readystatechange",i),e.setRequestHeader=new Proxy(e.setRequestHeader,{apply(t,e,n){const[r,o]=n,s=e[Cn];return s&&f(r)&&f(o)&&(s.request_headers[r.toLowerCase()]=o),t.apply(e,n)}}),t.apply(e,n)}}),t.send=new Proxy(t.send,{apply(t,e,n){const r=e[Cn];if(!r)return t.apply(e,n);void 0!==n[0]&&(r.body=n[0]);return Ge("xhr",{startTimestamp:1e3*ot(),xhr:e}),t.apply(e,n)}})}const An=100;function Ln(t,e){const n=bt(),r=Et();if(!n)return;const{beforeBreadcrumb:o=null,maxBreadcrumbs:s=An}=n.getOptions();if(s<=0)return;const a={timestamp:rt(),...t},c=o?i((()=>o(a,e))):a;null!==c&&(n.emit&&n.emit("beforeAddBreadcrumb",c,e),r.addBreadcrumb(c,s))}function Mn(){"console"in n&&o.forEach((function(t){t in n.console&&D(n.console,t,(function(e){return s[t]=e,function(...e){Ge("console",{args:e,level:t});const r=s[t];r&&r.apply(n.console,e)}}))}))}function Un(e,r=!1){r&&!function(){if("string"==typeof EdgeRuntime)return!0;if(!Ue())return!1;if(qe(Me.fetch))return!0;let e=!1;const n=Me.document;if(n&&"function"==typeof n.createElement)try{const t=n.createElement("iframe");t.hidden=!0,n.head.appendChild(t),t.contentWindow&&t.contentWindow.fetch&&(e=qe(t.contentWindow.fetch)),n.head.removeChild(t)}catch(e){t&&a.warn("Could not create sandbox iframe for pure fetch check, bailing to window.fetch: ",e)}return e}()||D(n,"fetch",(function(t){return function(...r){const{method:o,url:s}=function(t){if(0===t.length)return{method:"GET",url:""};if(2===t.length){const[e,n]=t;return{url:Fn(e),method:qn(n,"method")?String(n.method).toUpperCase():"GET"}}const e=t[0];return{url:Fn(e),method:qn(e,"method")?String(e.method).toUpperCase():"GET"}}(r),i={args:r,fetchData:{method:o,url:s},startTimestamp:1e3*ot()};e||Ge("fetch",{...i});const a=(new Error).stack;return t.apply(n,r).then((async t=>(e?e(t):Ge("fetch",{...i,endTimestamp:1e3*ot(),response:t}),t)),(t=>{throw Ge("fetch",{...i,endTimestamp:1e3*ot(),error:t}),u(t)&&void 0===t.stack&&(t.stack=a,N(t,"framesToPop",1)),t}))}}))}function qn(t,e){return!!t&&"object"==typeof t&&!!t[e]}function Fn(t){return"string"==typeof t?t:t?qn(t,"url")?t.url:t.toString?t.toString():"":""}const Yn=["fatal","error","warning","log","info","debug"];function Bn(t){return"warn"===t?"warning":Yn.includes(t)?t:"log"}function Hn(t){return void 0===t?void 0:t>=400&&t<500?"warning":t>=500?"error":void 0}function Gn(t){if(!t)return{};const e=t.match(/^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/);if(!e)return{};const n=e[6]||"",r=e[8]||"";return{host:e[4],path:e[5],protocol:e[2],search:n,hash:r,relative:e[5]+n+r}}const Wn=1024,zn=(t={})=>{const e={console:!0,dom:!0,fetch:!0,history:!0,sentry:!0,xhr:!0,...t};return{name:"Breadcrumbs",setup(t){var n;e.console&&function(t){const e="console";Be(e,t),He(e,Mn)}(function(t){return function(e){if(bt()!==t)return;const n={category:"console",data:{arguments:e.args,logger:"console"},level:Bn(e.level),message:$(e.args," ")};if("assert"===e.level){if(!1!==e.args[0])return;n.message=`Assertion failed: ${$(e.args.slice(1)," ")||"console.assert"}`,n.data.arguments=e.args.slice(1)}Ln(n,{input:e.args,level:e.level})}}(t)),e.dom&&(n=function(t,e){return function(n){if(bt()!==t)return;let r,o,s="object"==typeof e?e.serializeAttribute:void 0,i="object"==typeof e&&"number"==typeof e.maxStringLength?e.maxStringLength:void 0;i&&i>Wn&&(fn&&a.warn(`\`dom.maxStringLength\` cannot exceed 1024, but a value of ${i} was configured. Sentry will use 1024 instead.`),i=Wn),"string"==typeof s&&(s=[s]);try{const t=n.event,e=function(t){return!!t&&!!t.target}(t)?t.target:t;r=x(e,{keyAttrs:s,maxStringLength:i}),o=function(t){if(!b.HTMLElement)return null;let e=t;for(let t=0;t<5;t++){if(!e)return null;if(e instanceof HTMLElement){if(e.dataset.sentryComponent)return e.dataset.sentryComponent;if(e.dataset.sentryElement)return e.dataset.sentryElement}e=e.parentNode}return null}(e)}catch(t){r=""}if(0===r.length)return;const c={category:`ui.${n.name}`,message:r};o&&(c.data={"ui.component_name":o}),Ln(c,{event:n.event,name:n.name,global:n.global})}}(t,e.dom),Be("dom",n),He("dom",Pn)),e.xhr&&function(t){Be("xhr",t),He("xhr",jn)}(function(t){return function(e){if(bt()!==t)return;const{startTimestamp:n,endTimestamp:r}=e,o=e.xhr[Cn];if(!n||!r||!o)return;const{method:s,url:i,status_code:a,body:c}=o,u={method:s,url:i,status_code:a},l={xhr:e.xhr,input:c,startTimestamp:n,endTimestamp:r};Ln({category:"xhr",data:u,type:"http",level:Hn(a)},l)}}(t)),e.fetch&&function(t,e){const n="fetch";Be(n,t),He(n,(()=>Un(void 0,e)))}(function(t){return function(e){if(bt()!==t)return;const{startTimestamp:n,endTimestamp:r}=e;if(r&&(!e.fetchData.url.match(/sentry_key/)||"POST"!==e.fetchData.method))if(e.error){Ln({category:"fetch",data:e.fetchData,level:"error",type:"http"},{data:e.error,input:e.args,startTimestamp:n,endTimestamp:r})}else{const t=e.response,o={...e.fetchData,status_code:t&&t.status},s={input:e.args,response:t,startTimestamp:n,endTimestamp:r};Ln({category:"fetch",data:o,type:"http",level:Hn(o.status_code)},s)}}}(t)),e.history&&Ve(function(t){return function(e){if(bt()!==t)return;let n=e.from,r=e.to;const o=Gn(xn.location.href);let s=n?Gn(n):void 0;const i=Gn(r);s&&s.path||(s=o),o.protocol===i.protocol&&o.host===i.host&&(r=i.relative),o.protocol===s.protocol&&o.host===s.host&&(n=s.relative),Ln({category:"navigation",data:{from:n,to:r}})}}(t)),e.sentry&&t.on("beforeSendEvent",function(t){return function(e){bt()===t&&Ln({category:"sentry."+("transaction"===e.type?"transaction":"event"),event_id:e.event_id,level:e.level,message:q(e)},{event:e})}}(t))}}};const Jn=["EventTarget","Window","Node","ApplicationCache","AudioTrackList","BroadcastChannel","ChannelMergerNode","CryptoOperation","EventSource","FileReader","HTMLUnknownElement","IDBDatabase","IDBRequest","IDBTransaction","KeyOperation","MediaController","MessagePort","ModalWindow","Notification","SVGElementInstance","Screen","SharedWorker","TextTrack","TextTrackCue","TextTrackList","WebSocket","WebSocketWorker","Worker","XMLHttpRequest","XMLHttpRequestEventTarget","XMLHttpRequestUpload"],Vn=(t={})=>{const e={XMLHttpRequest:!0,eventTarget:!0,requestAnimationFrame:!0,setInterval:!0,setTimeout:!0,...t};return{name:"BrowserApiErrors",setupOnce(){e.setTimeout&&D(xn,"setTimeout",Kn),e.setInterval&&D(xn,"setInterval",Kn),e.requestAnimationFrame&&D(xn,"requestAnimationFrame",Xn),e.XMLHttpRequest&&"XMLHttpRequest"in xn&&D(XMLHttpRequest.prototype,"send",Qn);const t=e.eventTarget;if(t){(Array.isArray(t)?t:Jn).forEach(Zn)}}}};function Kn(t){return function(...e){const n=e[0];return e[0]=$n(n,{mechanism:{data:{function:Pt(t)},handled:!1,type:"instrument"}}),t.apply(this,e)}}function Xn(t){return function(e){return t.apply(this,[$n(e,{mechanism:{data:{function:"requestAnimationFrame",handler:Pt(t)},handled:!1,type:"instrument"}})])}}function Qn(t){return function(...e){const n=this;return["onload","onerror","onprogress","onreadystatechange"].forEach((t=>{t in n&&"function"==typeof n[t]&&D(n,t,(function(e){const n={mechanism:{data:{function:t,handler:Pt(e)},handled:!1,type:"instrument"}},r=P(e);return r&&(n.mechanism.data.handler=Pt(r)),$n(e,n)}))})),t.apply(this,e)}}function Zn(t){const e=xn,n=e[t]&&e[t].prototype;n&&n.hasOwnProperty&&n.hasOwnProperty("addEventListener")&&(D(n,"addEventListener",(function(e){return function(n,r,o){try{"function"==typeof r.handleEvent&&(r.handleEvent=$n(r.handleEvent,{mechanism:{data:{function:"handleEvent",handler:Pt(r),target:t},handled:!1,type:"instrument"}}))}catch(t){}return e.apply(this,[n,$n(r,{mechanism:{data:{function:"addEventListener",handler:Pt(r),target:t},handled:!1,type:"instrument"}}),o])}})),D(n,"removeEventListener",(function(t){return function(e,n,r){const o=n;try{const n=o&&o.__sentry_wrapped__;n&&t.call(this,e,n,r)}catch(t){}return t.call(this,e,o,r)}})))}let tr=null;function er(){tr=n.onerror,n.onerror=function(t,e,n,r,o){return Ge("error",{column:r,error:o,line:n,msg:t,url:e}),!(!tr||tr.__SENTRY_LOADER__)&&tr.apply(this,arguments)},n.onerror.__SENTRY_INSTRUMENTED__=!0}let nr=null;function rr(){nr=n.onunhandledrejection,n.onunhandledrejection=function(t){return Ge("unhandledrejection",t),!(nr&&!nr.__SENTRY_LOADER__)||nr.apply(this,arguments)},n.onunhandledrejection.__SENTRY_INSTRUMENTED__=!0}const or=(t={})=>{const e={onerror:!0,onunhandledrejection:!0,...t};return{name:"GlobalHandlers",setupOnce(){Error.stackTraceLimit=50},setup(t){e.onerror&&(!function(t){!function(t){const e="error";Be(e,t),He(e,er)}((e=>{const{stackParser:n,attachStacktrace:r}=ir();if(bt()!==t||kn())return;const{msg:o,url:s,line:i,column:a,error:c}=e,u=function(t,e,n,r){const o=t.exception=t.exception||{},s=o.values=o.values||[],i=s[0]=s[0]||{},a=i.stacktrace=i.stacktrace||{},c=a.frames=a.frames||[],u=isNaN(parseInt(r,10))?void 0:r,l=isNaN(parseInt(n,10))?void 0:n,p=f(e)&&e.length>0?e:function(){try{return b.document.location.href}catch(t){return""}}();0===c.length&&c.push({colno:u,filename:p,function:$t,in_app:!0,lineno:l});return t}(En(n,c||o,void 0,r,!1),s,i,a);u.level="error",ke(u,{originalException:c,mechanism:{handled:!1,type:"onerror"}})}))}(t),sr("onerror")),e.onunhandledrejection&&(!function(t){!function(t){const e="unhandledrejection";Be(e,t),He(e,rr)}((e=>{const{stackParser:n,attachStacktrace:r}=ir();if(bt()!==t||kn())return;const o=function(t){if(m(t))return t;try{if("reason"in t)return t.reason;if("detail"in t&&"reason"in t.detail)return t.detail.reason}catch(t){}return t}(e),s=m(o)?{exception:{values:[{type:"UnhandledRejection",value:`Non-Error promise rejection captured with value: ${String(o)}`}]}}:En(n,o,void 0,r,!0);s.level="error",ke(s,{originalException:o,mechanism:{handled:!1,type:"onunhandledrejection"}})}))}(t),sr("onunhandledrejection"))}}};function sr(t){fn&&a.log(`Global Handler attached: ${t}`)}function ir(){const t=bt();return t&&t.getOptions()||{stackParser:()=>[],attachStacktrace:!1}}const ar=()=>({name:"HttpContext",preprocessEvent(t){if(!xn.navigator&&!xn.location&&!xn.document)return;const e=t.request&&t.request.url||xn.location&&xn.location.href,{referrer:n}=xn.document||{},{userAgent:r}=xn.navigator||{},o={...t.request&&t.request.headers,...n&&{Referer:n},...r&&{"User-Agent":r}},s={...t.request,...e&&{url:e},headers:o};t.request=s}});function cr(t,e,n=250,r,o,s,i){if(!(s.exception&&s.exception.values&&i&&v(i.originalException,Error)))return;const a=s.exception.values.length>0?s.exception.values[s.exception.values.length-1]:void 0;var c,u;a&&(s.exception.values=(c=ur(t,e,o,i.originalException,r,s.exception.values,a,0),u=n,c.map((t=>(t.value&&(t.value=k(t.value,u)),t)))))}function ur(t,e,n,r,o,s,i,a){if(s.length>=n+1)return s;let c=[...s];if(v(r[o],Error)){lr(i,a);const s=t(e,r[o]),u=c.length;pr(s,o,u,a),c=ur(t,e,n,r[o],o,[s,...c],s,u)}return Array.isArray(r.errors)&&r.errors.forEach(((r,s)=>{if(v(r,Error)){lr(i,a);const u=t(e,r),l=c.length;pr(u,`errors[${s}]`,l,a),c=ur(t,e,n,r,o,[u,...c],u,l)}})),c}function lr(t,e){t.mechanism=t.mechanism||{type:"generic",handled:!0},t.mechanism={...t.mechanism,..."AggregateError"===t.type&&{is_exception_group:!0},exception_id:e}}function pr(t,e,n,r){t.mechanism=t.mechanism||{type:"generic",handled:!0},t.mechanism={...t.mechanism,type:"chained",source:e,exception_id:n,parent_id:r}}const dr=(t={})=>{const e=t.limit||5,n=t.key||"cause";return{name:"LinkedErrors",preprocessEvent(t,r,o){const s=o.getOptions();cr(hn,s.stackParser,s.maxValueLength,n,e,t,r)}}};function fr(t,e,n,r){const o={filename:t,function:""===e?$t:e,in_app:!0};return void 0!==n&&(o.lineno=n),void 0!==r&&(o.colno=r),o}const hr=/^\s*at (\S+?)(?::(\d+))(?::(\d+))\s*$/i,mr=/^\s*at (?:(.+?\)(?: \[.+\])?|.*?) ?\((?:address at )?)?(?:async )?((?:|[-a-z]+:|.*bundle|\/)?.*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,_r=/\((\S*)(?::(\d+))(?::(\d+))\)/,gr=/^\s*(.*?)(?:\((.*?)\))?(?:^|@)?((?:[-a-z]+)?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js)|\/[\w\-. /=]+)(?::(\d+))?(?::(\d+))?\s*$/i,yr=/(\S+) line (\d+)(?: > eval line \d+)* > eval/i,vr=Dt(...[[30,t=>{const e=hr.exec(t);if(e){const[,t,n,r]=e;return fr(t,$t,+n,+r)}const n=mr.exec(t);if(n){if(n[2]&&0===n[2].indexOf("eval")){const t=_r.exec(n[2]);t&&(n[2]=t[1],n[3]=t[2],n[4]=t[3])}const[t,e]=Er(n[1]||$t,n[2]);return fr(e,t,n[3]?+n[3]:void 0,n[4]?+n[4]:void 0)}}],[50,t=>{const e=gr.exec(t);if(e){if(e[3]&&e[3].indexOf(" > eval")>-1){const t=yr.exec(e[3]);t&&(e[1]=e[1]||"eval",e[3]=t[1],e[4]=t[2],e[5]="")}let t=e[3],n=e[1]||$t;return[n,t]=Er(n,t),fr(t,n,e[4]?+e[4]:void 0,e[5]?+e[5]:void 0)}}]]),Er=(t,e)=>{const n=-1!==t.indexOf("safari-extension"),r=-1!==t.indexOf("safari-web-extension");return n||r?[-1!==t.indexOf("@")?t.split("@")[0]:$t,n?`safari-extension:${e}`:`safari-web-extension:${e}`]:[t,e]},br="undefined"==typeof __SENTRY_DEBUG__||__SENTRY_DEBUG__,Sr={};function xr(t){const e=Sr[t];if(e)return e;let n=ze[t];if(qe(n))return Sr[t]=n.bind(ze);const r=ze.document;if(r&&"function"==typeof r.createElement)try{const e=r.createElement("iframe");e.hidden=!0,r.head.appendChild(e);const o=e.contentWindow;o&&o[t]&&(n=o[t]),r.head.removeChild(e)}catch(e){br&&a.warn(`Could not create sandbox iframe for ${t} check, bailing to window.${t}: `,e)}return n?Sr[t]=n.bind(ze):n}function wr(t){Sr[t]=void 0}function kr(t){const e=[];function n(t){return e.splice(e.indexOf(t),1)[0]||Promise.resolve(void 0)}return{$:e,add:function(r){if(!(void 0===t||e.lengthn(o))).then(null,(()=>n(o).then(null,(()=>{})))),o},drain:function(t){return new Wt(((n,r)=>{let o=e.length;if(!o)return n(!0);const s=setTimeout((()=>{t&&t>0&&n(!1)}),t);e.forEach((t=>{Ht(t).then((()=>{--o||(clearTimeout(s),n(!0))}),r)}))}))}}}const $r=6e4;function Or(t,{statusCode:e,headers:n},r=Date.now()){const o={...t},s=n&&n["x-sentry-rate-limits"],i=n&&n["retry-after"];if(s)for(const t of s.trim().split(",")){const[e,n,,,s]=t.split(":",5),i=parseInt(e,10),a=1e3*(isNaN(i)?60:i);if(n)for(const t of n.split(";"))"metric_bucket"===t&&s&&!s.split(";").includes("custom")||(o[t]=r+a);else o.all=r+a}else i?o.all=r+function(t,e=Date.now()){const n=parseInt(`${t}`,10);if(!isNaN(n))return 1e3*n;const r=Date.parse(`${t}`);return isNaN(r)?$r:r-e}(i,r):429===e&&(o.all=r+6e4);return o}const Tr=64;function Dr(t,e,n=kr(t.bufferSize||Tr)){let r={};return{send:function(o){const s=[];if(Ze(o,((e,n)=>{const o=on(n);if(function(t,e,n=Date.now()){return function(t,e){return t[e]||t.all||0}(t,e)>n}(r,o)){const r=Nr(e,n);t.recordDroppedEvent("ratelimit_backoff",o,r)}else s.push(e)})),0===s.length)return Ht({});const i=Xe(o[0],s),c=e=>{Ze(i,((n,r)=>{const o=Nr(n,r);t.recordDroppedEvent(e,on(r),o)}))};return n.add((()=>e({body:en(i)}).then((t=>(void 0!==t.statusCode&&(t.statusCode<200||t.statusCode>=300)&&G&&a.warn(`Sentry responded with status code ${t.statusCode} to sent event.`),r=Or(r,t),t)),(t=>{throw c("network_error"),t})))).then((t=>t),(t=>{if(t instanceof an)return G&&a.error("Skipped sending event because buffer is full."),c("queue_overflow"),Ht({});throw t}))},flush:t=>n.drain(t)}}function Nr(t,e){if("event"===e||"transaction"===e)return Array.isArray(t)?t[1]:void 0}function Ir(t,e=xr("fetch")){let n=0,r=0;return Dr(t,(function(o){const s=o.body.length;n+=s,r++;const i={body:o.body,method:"POST",referrerPolicy:"origin",headers:t.headers,keepalive:n<=6e4&&r<15,...t.fetchOptions};if(!e)return wr("fetch"),Gt("No fetch implementation available");try{return e(t.url,i).then((t=>(n-=s,r--,{statusCode:t.status,headers:{"x-sentry-rate-limits":t.headers.get("X-Sentry-Rate-Limits"),"retry-after":t.headers.get("Retry-After")}})))}catch(t){return wr("fetch"),n-=s,r--,Gt(t)}}))}const Pr={init:function(t={}){const e=function(t={}){const e={defaultIntegrations:[X(),wt(),Vn(),zn(),or(),dr(),Ct(),ar()],release:"string"==typeof __SENTRY_RELEASE__?__SENTRY_RELEASE__:xn.SENTRY_RELEASE&&xn.SENTRY_RELEASE.id?xn.SENTRY_RELEASE.id:void 0,autoSessionTracking:!0,sendClientReports:!0};return null==t.defaultIntegrations&&delete t.defaultIntegrations,{...e,...t}}(t);if(function(){const t=void 0!==xn.window&&xn;if(!t)return!1;const e=t[t.chrome?"chrome":"browser"],n=e&&e.runtime&&e.runtime.id,r=xn.location&&xn.location.href||"",o=!!n&&xn===xn.top&&["chrome-extension:","moz-extension:","ms-browser-extension:","safari-web-extension:"].some((t=>r.startsWith(`${t}//`))),s=void 0!==t.nw;return!!n&&!o&&!s}())return void i((()=>{console.error("[Sentry] You cannot run Sentry this way in a browser extension, check: https://docs.sentry.io/platforms/javascript/best-practices/browser-extensions/")}));fn&&(Ue()||a.warn("No Fetch API detected. The Sentry SDK requires a Fetch API compatible environment to send events. Please add a Fetch API polyfill."));const n={...e,stackParser:(r=e.stackParser||vr,Array.isArray(r)?Dt(...r):r),integrations:z(e),transport:e.transport||Ir};var r;const o=Mt(On,n);return e.autoSessionTracking&&function(){if(void 0===xn.document)return void(fn&&a.warn("Session tracking in non-browser environment with @sentry/browser is not supported."));$e({ignoreDuration:!0}),De(),Ve((({from:t,to:e})=>{void 0!==t&&t!==e&&($e({ignoreDuration:!0}),De())}))}(),o},showReportDialog:function(t={}){if(!xn.document)return void(fn&&a.error("Global document not defined in showReportDialog call"));const e=vt(),n=e.getClient(),r=n&&n.getDsn();if(!r)return void(fn&&a.error("DSN not configured for showReportDialog call"));if(e&&(t.user={...e.getUser(),...t.user}),!t.eventId){const e=Et().lastEventId();e&&(t.eventId=e)}const o=xn.document.createElement("script");o.async=!0,o.crossOrigin="anonymous",o.src=function(t,e){const n=Re(t);if(!n)return"";const r=`${je(n)}embed/error-page/`;let o=`dsn=${Ie(n)}`;for(const t in e)if("dsn"!==t&&"onClose"!==t)if("user"===t){const t=e.user;if(!t)continue;t.name&&(o+=`&name=${encodeURIComponent(t.name)}`),t.email&&(o+=`&email=${encodeURIComponent(t.email)}`)}else o+=`&${encodeURIComponent(t)}=${encodeURIComponent(e[t])}`;return`${r}?${o}`}(r,t),t.onLoad&&(o.onload=t.onLoad);const{onClose:s}=t;if(s){const t=e=>{if("__sentry_reportdialog_closed__"===e.data)try{s()}finally{xn.removeEventListener("message",t)}};xn.addEventListener("message",t)}const i=xn.document.head||xn.document.body;i?i.appendChild(o):fn&&a.error("Not injecting report dialog. No injection point found in HTML")}};window.Sentry=Pr})(); \ No newline at end of file From b9b70f07b2c3300ee07a1e6edb2ab48f34b7fec4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 22:06:23 +0000 Subject: [PATCH 60/61] chore(deps): update dependency boto3-stubs to v1.35.32 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1030f90b165d..705360c46e11 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -144,7 +144,7 @@ mercurial = [ "mercurial>=6.8.0,<7.0" ] mypy = [ - "boto3-stubs==1.35.31", + "boto3-stubs==1.35.32", "celery-types==0.22.0", "django-stubs-ext==5.1.0", "django-stubs==5.1.0", From 16a2c68c3b6abe928de293edf90509b30927e68a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 3 Oct 2024 03:39:04 +0000 Subject: [PATCH 61/61] chore(deps): update dependency types-python-dateutil to v2.9.0.20241003 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 705360c46e11..c27851166bd3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -154,7 +154,7 @@ mypy = [ "types-jsonschema==4.23.0.20240813", "types-openpyxl==3.1.5.20240918", "types-Pillow==10.2.0.20240822", - "types-python-dateutil==2.9.0.20240906", + "types-python-dateutil==2.9.0.20241003", "types-requests==2.32.0.20240914" ] mysql = [