Compare commits

..

61 Commits

Author SHA1 Message Date
Hafiz
cd06148422 lint fix 2025-08-12 12:28:10 -05:00
Hafiz
a0b179561b Update ToS error message
- Updated account suspension message from "This account, User ID..." to "Your account @[username] has been
  blocked..."
- Modified server auth middleware to pass username parameter when throwing account suspended error
-Modified auth utils loginRes function to include username in suspended account error
- Updated client bannedAccountModal component to pass username (empty string if unavailable)
- Updated login test to expect username in account suspended message
2025-08-12 12:23:46 -05:00
Hafiz
9a1fb18959 Merge remote-tracking branch 'origin/develop' into qa/bat 2025-08-12 09:46:07 -05:00
Kalista Payne
876d5a67d6 5.38.2 2025-08-08 14:04:19 -05:00
Kalista Payne
3078af8f2a fix(apple): don't run auth middleware during redirect 2025-08-08 14:04:13 -05:00
Weblate
dad1440138 Translated using Weblate (German)
Currently translated at 99.4% (185 of 186 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Dutch)

Currently translated at 78.0% (2643 of 3385 strings)

Translated using Weblate (Dutch)

Currently translated at 40.8% (100 of 245 strings)

Translated using Weblate (Polish)

Currently translated at 89.9% (233 of 259 strings)

Translated using Weblate (Dutch)

Currently translated at 67.5% (175 of 259 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 20.8% (51 of 245 strings)

Translated using Weblate (Turkish)

Currently translated at 65.9% (60 of 91 strings)

Translated using Weblate (Turkish)

Currently translated at 65.9% (60 of 91 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 17.9% (44 of 245 strings)

Co-authored-by: FingerTiao <787170918@qq.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Karmelkowy <kicimeow.karmelio@gmail.com>
Co-authored-by: Linsey Dunya Pastoor <sekai.creations@gmail.com>
Co-authored-by: Mete Olmez <metezori27@gmail.com>
Co-authored-by: Sefa Uğurlu <ugurlusefa2@gmail.com>
Co-authored-by: Summer_GUI <heyang94@163.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: innnko <ayakabooker@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/death/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/front/de/
Translate-URL: https://translate.habitica.com/projects/habitica/front/es/
Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/pl/
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Communityguidelines
Translation: Habitica/Contrib
Translation: Habitica/Death
Translation: Habitica/Defaulttasks
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Settings
2025-08-08 10:12:25 +02:00
Phillip Thelen
12773d539e Add interface to block ip-addresses or clients due to abuse (#15484)
* Read IP blocks from database

* begin building general blocking solution

* add new frontend files

* Add UI for managing blockers

* correctly reset local data after creating blocker

* Tweak wording

* Add UI for managing blockers

* restructure admin pages

* improve test coverage

* Improve blocker UI

* add blocker to block emails from registration

* lint fix

* fix

* lint fixes

* fix import

* add new permission for managing blockers

* improve permission check

* fix managing permissions from admin

* improve navbar display for non fullAccess admin

* update block error strings

* lint fix

* add option to errorHandler to skip logging

* validate blocker value during input

* improve blocker form display

* chore(subproj): reconcile habitica-images

* fix(scripts): use same Mongo version for dev/test

* fix(whitespace): eof

* documentation improvements

* remove nconf import

* remove old test

---------

Co-authored-by: Kalista Payne <kalista@habitica.com>
Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-08-06 15:08:07 -05:00
Fiz
ae4130b108 Add backend support for Hydra mount (#15482)
* chore: update time travelers shop to display seasonal backgrounds

* chore: update time travelers banner (note CSS borken rn)

* chore: fix borken CSS and update logic in shop

* chore: added isSubscribed function, not working

* chore: isSubscribed working but no bg for subscribers

* chore: logic and css updates

* chore: update habitica-images

* chore: add check for trinket

* chore: more time traveler shop logicking

* Add backend support for Hydra mount

- Add Dragon-Hydra to special mounts in stable.js
  - Configure as contributor level 7 reward with canFind: true
  - Add GIF format support for mount sprites
  - Enable admin panel granting capability

* Fix Vue template errors in timeTravelers component

* Fix duplicate template block in timeTravelers component

* add CSS for Hydra mount GIF sprites

Added CSS rules for Mount_Head_Dragon-Hydra and Mount_Body_Dragon-Hydra GIF sprites

* Remove the separate Hydra mount dimension declaration

---------

Co-authored-by: CuriousMagpie <eilatan@gmail.com>
2025-08-05 15:12:44 -05:00
Kalista Payne
ad0614282e 5.38.1 2025-08-05 14:31:05 -05:00
Phillip Thelen
2ea0b64603 improve blocker form display 2025-08-05 14:57:25 +02:00
Phillip Thelen
bd1aa1e417 validate blocker value during input 2025-08-05 14:45:23 +02:00
Weblate
5a7704aed7 Translated using Weblate (Chinese (Traditional))
Currently translated at 17.1% (42 of 245 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 16.7% (41 of 245 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 16.3% (40 of 245 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Japanese)

Currently translated at 98.8% (425 of 430 strings)

Translated using Weblate (French)

Currently translated at 99.4% (184 of 185 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 15.9% (39 of 245 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (3385 of 3385 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (185 of 185 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.8% (3379 of 3385 strings)

Translated using Weblate (Polish)

Currently translated at 95.5% (128 of 134 strings)

Translated using Weblate (Japanese)

Currently translated at 94.7% (254 of 268 strings)

Translated using Weblate (Polish)

Currently translated at 94.0% (126 of 134 strings)

Translated using Weblate (Japanese)

Currently translated at 98.6% (424 of 430 strings)

Translated using Weblate (Japanese)

Currently translated at 98.3% (423 of 430 strings)

Translated using Weblate (Japanese)

Currently translated at 92.5% (798 of 862 strings)

Translated using Weblate (Japanese)

Currently translated at 92.4% (797 of 862 strings)

Translated using Weblate (Japanese)

Currently translated at 90.6% (781 of 862 strings)

Translated using Weblate (Japanese)

Currently translated at 91.9% (3112 of 3385 strings)

Translated using Weblate (Japanese)

Currently translated at 91.9% (3111 of 3385 strings)

Translated using Weblate (Japanese)

Currently translated at 94.0% (174 of 185 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 15.5% (38 of 245 strings)

Translated using Weblate (Japanese)

Currently translated at 91.6% (3104 of 3385 strings)

Translated using Weblate (Japanese)

Currently translated at 93.5% (173 of 185 strings)

Translated using Weblate (Japanese)

Currently translated at 99.6% (279 of 280 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Japanese)

Currently translated at 89.2% (769 of 862 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Japanese)

Currently translated at 94.4% (253 of 268 strings)

Translated using Weblate (Japanese)

Currently translated at 91.8% (170 of 185 strings)

Translated using Weblate (Japanese)

Currently translated at 97.9% (421 of 430 strings)

Translated using Weblate (Japanese)

Currently translated at 91.6% (3104 of 3385 strings)

Translated using Weblate (Japanese)

Currently translated at 93.6% (251 of 268 strings)

Translated using Weblate (Japanese)

Currently translated at 90.8% (168 of 185 strings)

Translated using Weblate (Japanese)

Currently translated at 82.4% (202 of 245 strings)

Translated using Weblate (French)

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3385 of 3385 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 15.1% (37 of 245 strings)

Translated using Weblate (French)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Japanese)

Currently translated at 91.3% (3092 of 3385 strings)

Translated using Weblate (Japanese)

Currently translated at 92.5% (248 of 268 strings)

Translated using Weblate (Japanese)

Currently translated at 92.5% (248 of 268 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Korean)

Currently translated at 22.8% (56 of 245 strings)

Translated using Weblate (Korean)

Currently translated at 47.7% (128 of 268 strings)

Translated using Weblate (Croatian)

Currently translated at 45.1% (121 of 268 strings)

Translated using Weblate (Korean)

Currently translated at 71.9% (620 of 862 strings)

Translated using Weblate (Croatian)

Currently translated at 70.6% (609 of 862 strings)

Translated using Weblate (Croatian)

Currently translated at 75.0% (6 of 8 strings)

Translated using Weblate (Korean)

Currently translated at 67.6% (291 of 430 strings)

Translated using Weblate (Korean)

Currently translated at 52.8% (1788 of 3385 strings)

Translated using Weblate (Croatian)

Currently translated at 50.3% (1706 of 3385 strings)

Translated using Weblate (Croatian)

Currently translated at 51.7% (134 of 259 strings)

Translated using Weblate (Czech)

Currently translated at 92.8% (130 of 140 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 86.9% (233 of 268 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Danish)

Currently translated at 92.1% (105 of 114 strings)

Translated using Weblate (Czech)

Currently translated at 89.4% (102 of 114 strings)

Translated using Weblate (Czech)

Currently translated at 83.5% (112 of 134 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 71.6% (308 of 430 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Serbian)

Currently translated at 84.4% (49 of 58 strings)

Translated using Weblate (Bulgarian)

Currently translated at 51.4% (144 of 280 strings)

Translated using Weblate (Swedish)

Currently translated at 66.5% (286 of 430 strings)

Translated using Weblate (Serbian)

Currently translated at 65.5% (282 of 430 strings)

Translated using Weblate (Slovak)

Currently translated at 65.5% (282 of 430 strings)

Translated using Weblate (Romanian)

Currently translated at 66.7% (287 of 430 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (Danish)

Currently translated at 66.0% (284 of 430 strings)

Translated using Weblate (Czech)

Currently translated at 69.7% (300 of 430 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.7% (3377 of 3385 strings)

Translated using Weblate (Swedish)

Currently translated at 54.1% (1834 of 3385 strings)

Translated using Weblate (Serbian)

Currently translated at 50.6% (1714 of 3385 strings)

Translated using Weblate (Slovak)

Currently translated at 50.0% (1695 of 3385 strings)

Translated using Weblate (Romanian)

Currently translated at 60.5% (2050 of 3385 strings)

Translated using Weblate (Hebrew)

Currently translated at 38.4% (1301 of 3385 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3385 of 3385 strings)

Translated using Weblate (Danish)

Currently translated at 54.0% (1829 of 3385 strings)

Translated using Weblate (Czech)

Currently translated at 59.6% (2020 of 3385 strings)

Translated using Weblate (Swedish)

Currently translated at 75.6% (140 of 185 strings)

Translated using Weblate (Serbian)

Currently translated at 73.5% (136 of 185 strings)

Translated using Weblate (Slovak)

Currently translated at 84.8% (157 of 185 strings)

Translated using Weblate (Romanian)

Currently translated at 78.9% (146 of 185 strings)

Translated using Weblate (Portuguese)

Currently translated at 82.1% (152 of 185 strings)

Translated using Weblate (Italian)

Currently translated at 91.8% (170 of 185 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (185 of 185 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (185 of 185 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (185 of 185 strings)

Translated using Weblate (German)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (Danish)

Currently translated at 77.2% (143 of 185 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 98.7% (242 of 245 strings)

Translated using Weblate (Czech)

Currently translated at 75.1% (139 of 185 strings)

Translated using Weblate (Bulgarian)

Currently translated at 74.5% (138 of 185 strings)

Translated using Weblate (Czech)

Currently translated at 8.1% (20 of 245 strings)

Translated using Weblate (Swedish)

Currently translated at 72.0% (621 of 862 strings)

Translated using Weblate (Serbian)

Currently translated at 65.1% (562 of 862 strings)

Translated using Weblate (Slovak)

Currently translated at 66.9% (577 of 862 strings)

Translated using Weblate (Romanian)

Currently translated at 77.7% (670 of 862 strings)

Translated using Weblate (Portuguese)

Currently translated at 70.0% (604 of 862 strings)

Translated using Weblate (Polish)

Currently translated at 67.1% (579 of 862 strings)

Translated using Weblate (Italian)

Currently translated at 86.8% (749 of 862 strings)

Translated using Weblate (Indonesian)

Currently translated at 86.0% (742 of 862 strings)

Translated using Weblate (Hebrew)

Currently translated at 66.1% (570 of 862 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 98.0% (845 of 862 strings)

Translated using Weblate (Danish)

Currently translated at 69.9% (603 of 862 strings)

Translated using Weblate (Czech)

Currently translated at 69.7% (601 of 862 strings)

Translated using Weblate (Bulgarian)

Currently translated at 66.3% (572 of 862 strings)

Translated using Weblate (Serbian)

Currently translated at 74.0% (305 of 412 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Danish)

Currently translated at 90.0% (371 of 412 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Swedish)

Currently translated at 53.6% (139 of 259 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Danish)

Currently translated at 62.1% (161 of 259 strings)

Translated using Weblate (Bulgarian)

Currently translated at 54.0% (140 of 259 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 82.8% (222 of 268 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 99.4% (184 of 185 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 98.3% (241 of 245 strings)

Translated using Weblate (Japanese)

Currently translated at 91.3% (3092 of 3385 strings)

Translated using Weblate (Japanese)

Currently translated at 88.4% (237 of 268 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Japanese)

Currently translated at 82.4% (202 of 245 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Japanese)

Currently translated at 87.3% (234 of 268 strings)

Translated using Weblate (Japanese)

Currently translated at 86.4% (160 of 185 strings)

Translated using Weblate (Japanese)

Currently translated at 99.8% (913 of 914 strings)

Translated using Weblate (German)

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (German)

Currently translated at 100.0% (3377 of 3377 strings)

Translated using Weblate (German)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (German)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (German)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (German)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3385 of 3385 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (268 of 268 strings)

Translated using Weblate (Russian)

Currently translated at 88.5% (248 of 280 strings)

Translated using Weblate (Spanish)

Currently translated at 99.8% (3379 of 3385 strings)

Translated using Weblate (German)

Currently translated at 100.0% (862 of 862 strings)

Co-authored-by: Ayaka Booker <ayakabooker@gmail.com>
Co-authored-by: Chaotic Lawful <habitica@eusebius.fr>
Co-authored-by: FingerTiao <787170918@qq.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Jan Freihöfer <jan.stauch.is@gmail.com>
Co-authored-by: Karmelkowy <kicimeow.karmelio@gmail.com>
Co-authored-by: Lio Zam <zerofux@web.de>
Co-authored-by: Mika <isekai.chr@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Summer_GUI <heyang94@163.com>
Co-authored-by: Vera <verasmolinap@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Zhi Hao Li <zhihaoli000@gmail.com>
Co-authored-by: Zuz Q <zuzannakunik@gmail.com>
Co-authored-by: innnko <ayakabooker@gmail.com>
Co-authored-by: 吳昀錡 <J1120241@gm.fdhs.tyc.edu.tw>
Co-authored-by: 潘致翰 <happyq0908@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/es/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/character/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/character/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/da/
Translate-URL: https://translate.habitica.com/projects/habitica/content/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/death/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/front/bg/
Translate-URL: https://translate.habitica.com/projects/habitica/front/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/front/da/
Translate-URL: https://translate.habitica.com/projects/habitica/front/de/
Translate-URL: https://translate.habitica.com/projects/habitica/front/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/front/es/
Translate-URL: https://translate.habitica.com/projects/habitica/front/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/front/it/
Translate-URL: https://translate.habitica.com/projects/habitica/front/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/front/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/front/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/front/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/front/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/front/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/da/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/he/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/da/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/bg/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/da/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/es/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/bg/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/cs/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/da/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/he/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/id/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/it/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/bg/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/da/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/de/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/es/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/cs/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Character
Translation: Habitica/Content
Translation: Habitica/Death
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Pets
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Settings
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2025-08-05 13:02:45 +02:00
Phillip Thelen
7c49b845d6 add option to errorHandler to skip logging 2025-08-04 17:40:26 +02:00
Phillip Thelen
1ee172139d lint fix 2025-08-04 16:32:40 +02:00
Phillip Thelen
6447b9ab4b update block error strings 2025-08-04 16:03:55 +02:00
Phillip Thelen
5c414099d9 improve navbar display for non fullAccess admin 2025-08-04 14:46:05 +02:00
Phillip Thelen
5e8e1179aa fix managing permissions from admin 2025-08-04 14:45:47 +02:00
Phillip Thelen
7e86a62624 improve permission check 2025-08-04 14:33:09 +02:00
Phillip Thelen
1ba9dda0ed add new permission for managing blockers 2025-08-04 14:21:36 +02:00
Fiz
2feadd6125 Merge pull request #15483 from HabitRPG/kalista/category-content
Add Challenge categories to content API
2025-07-31 12:38:01 -05:00
Phillip Thelen
227e5ceaa8 fix import 2025-07-30 11:26:55 +02:00
Phillip Thelen
f77ab5a3ab lint fixes 2025-07-30 11:26:55 +02:00
Phillip Thelen
1916faf647 fix 2025-07-30 11:26:55 +02:00
Phillip Thelen
80ecb5cef1 lint fix 2025-07-30 11:26:55 +02:00
Phillip Thelen
75c36e6622 add blocker to block emails from registration 2025-07-30 11:26:55 +02:00
Phillip Thelen
78330c975a Improve blocker UI 2025-07-30 11:26:55 +02:00
Phillip Thelen
95266f6cb3 improve test coverage 2025-07-30 11:26:55 +02:00
Phillip Thelen
e9b2c1b51a restructure admin pages 2025-07-30 11:26:54 +02:00
Phillip Thelen
2a2bea07ab Add UI for managing blockers 2025-07-30 11:26:54 +02:00
Phillip Thelen
ea60ddbf4c Tweak wording 2025-07-30 11:25:51 +02:00
Phillip Thelen
1c2ca0e478 correctly reset local data after creating blocker 2025-07-30 11:25:51 +02:00
Phillip Thelen
ef2b7eb928 Add UI for managing blockers 2025-07-30 11:25:51 +02:00
Phillip Thelen
3d16387a61 add new frontend files 2025-07-30 11:25:41 +02:00
Phillip Thelen
93b7770eaa begin building general blocking solution 2025-07-30 11:25:41 +02:00
Phillip Thelen
a9f84d3307 Read IP blocks from database 2025-07-30 11:25:41 +02:00
Kalista Payne
efe0b3cd9e 5.38.0 2025-07-28 14:15:00 -05:00
Kalista Payne
96731da380 5.37.3 2025-07-28 14:06:45 -05:00
Weblate
0c5dd5d8b5 Merge branch 'origin/develop' into Weblate. 2025-07-28 21:03:29 +02:00
Weblate
2f943a22e6 Translated using Weblate (German)
Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (German)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (German)

Currently translated at 100.0% (3377 of 3377 strings)

Translated using Weblate (German)

Currently translated at 100.0% (3377 of 3377 strings)

Translated using Weblate (German)

Currently translated at 100.0% (3377 of 3377 strings)

Translated using Weblate (German)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (German)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (German)

Currently translated at 98.6% (850 of 862 strings)

Translated using Weblate (German)

Currently translated at 99.8% (3373 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.8% (3373 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.8% (3373 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.5% (3361 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.5% (3361 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.5% (3361 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.4% (3360 of 3377 strings)

Translated using Weblate (German)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (185 of 185 strings)

Translated using Weblate (Polish)

Currently translated at 67.1% (579 of 862 strings)

Translated using Weblate (Polish)

Currently translated at 67.1% (579 of 862 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (German)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (German)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (German)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (German)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (German)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (German)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (German)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (German)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 14.2% (35 of 245 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 13.8% (34 of 245 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 13.0% (32 of 245 strings)

Translated using Weblate (Hebrew)

Currently translated at 2.0% (5 of 245 strings)

Translated using Weblate (Hebrew)

Currently translated at 66.1% (570 of 862 strings)

Translated using Weblate (Portuguese)

Currently translated at 54.1% (1830 of 3377 strings)

Co-authored-by: FingerTiao <787170918@qq.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Jan Freihöfer <jan.stauch.is@gmail.com>
Co-authored-by: Jonathan Niessen <37.friedrich@gmail.com>
Co-authored-by: Karmelkowy <kicimeow.karmelio@gmail.com>
Co-authored-by: Katharina <katharinaanna.wilding@gmail.com>
Co-authored-by: Laura Fleckenstein <fleckenstein_laura@web.de>
Co-authored-by: Omer I.S <omeritzicschwartz@gmail.com>
Co-authored-by: Remigiusz Haziak <haziakremigiusz@gmail.com>
Co-authored-by: Uwe B <hbtca@tunixgut.de>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Wellinton Cardoso <wmcardoso1@hotmail.com>
Co-authored-by: cloudzzy <truskawka412@gmail.com>
Co-authored-by: 吳昀錡 <J1120241@gm.fdhs.tyc.edu.tw>
Translate-URL: https://translate.habitica.com/projects/habitica/character/de/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/de/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/de/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/he/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/front/de/
Translate-URL: https://translate.habitica.com/projects/habitica/front/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/de/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/de/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/de/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/he/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pl/
Translation: Habitica/Character
Translation: Habitica/Communityguidelines
Translation: Habitica/Contrib
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Npc
Translation: Habitica/Questscontent
2025-07-28 21:03:17 +02:00
Phillip Thelen
666184d7e4 Fix 500 when deleting a very old group plan account (#15481) 2025-07-28 09:50:38 -05:00
Phillip Thelen
17d22dda3f enforce x-client header (#15476) 2025-07-22 14:00:51 -05:00
Natalie
d1a18c121d August 2025 Content Build (#15460)
* chore: 2025-08 content build

* chore: fix typos

* fix(string): degender the news

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-07-21 17:59:40 -05:00
Kalista Payne
836d7f3991 5.37.2 2025-07-21 09:01:59 -05:00
Weblate
ace9c3c46a Translated using Weblate (English (United Kingdom))
Currently translated at 82.7% (221 of 267 strings)

Translated using Weblate (Polish)

Currently translated at 67.0% (578 of 862 strings)

Translated using Weblate (Polish)

Currently translated at 67.0% (578 of 862 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 62.6% (57 of 91 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 71.4% (185 of 259 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 69.1% (179 of 259 strings)

Translated using Weblate (German)

Currently translated at 98.3% (181 of 184 strings)

Translated using Weblate (German)

Currently translated at 98.9% (191 of 193 strings)

Translated using Weblate (German)

Currently translated at 99.3% (3354 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.3% (3354 of 3377 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (412 of 412 strings)

Translated using Weblate (Japanese)

Currently translated at 89.0% (768 of 862 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 12.6% (31 of 245 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 12.6% (31 of 245 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 98.3% (181 of 184 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 78.7% (193 of 245 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 96.7% (237 of 245 strings)

Translated using Weblate (Italian)

Currently translated at 86.8% (749 of 862 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 99.3% (905 of 911 strings)

Translated using Weblate (German)

Currently translated at 99.2% (3352 of 3377 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 12.2% (30 of 245 strings)

Translated using Weblate (Japanese)

Currently translated at 82.0% (201 of 245 strings)

Translated using Weblate (German)

Currently translated at 99.2% (3350 of 3377 strings)

Translated using Weblate (Russian)

Currently translated at 88.9% (2994 of 3367 strings)

Translated using Weblate (Russian)

Currently translated at 88.8% (2991 of 3367 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 67.5% (175 of 259 strings)

Translated using Weblate (German)

Currently translated at 99.1% (3348 of 3377 strings)

Translated using Weblate (Japanese)

Currently translated at 85.8% (158 of 184 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Japanese)

Currently translated at 81.6% (200 of 245 strings)

Translated using Weblate (Spanish (Latin America))

Currently translated at 86.3% (787 of 911 strings)

Translated using Weblate (Japanese)

Currently translated at 79.1% (194 of 245 strings)

Translated using Weblate (Japanese)

Currently translated at 88.9% (767 of 862 strings)

Translated using Weblate (German)

Currently translated at 99.0% (3346 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.0% (3346 of 3377 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 94.6% (390 of 412 strings)

Translated using Weblate (Japanese)

Currently translated at 91.5% (3090 of 3377 strings)

Translated using Weblate (Japanese)

Currently translated at 85.3% (228 of 267 strings)

Translated using Weblate (Japanese)

Currently translated at 88.8% (766 of 862 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (412 of 412 strings)

Translated using Weblate (Japanese)

Currently translated at 77.5% (190 of 245 strings)

Translated using Weblate (Ukrainian)

Currently translated at 87.6% (234 of 267 strings)

Translated using Weblate (Ukrainian)

Currently translated at 57.6% (1946 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.0% (3345 of 3377 strings)

Translated using Weblate (German)

Currently translated at 98.3% (181 of 184 strings)

Translated using Weblate (German)

Currently translated at 99.0% (3345 of 3377 strings)

Translated using Weblate (German)

Currently translated at 99.0% (3345 of 3377 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.3% (427 of 430 strings)

Translated using Weblate (Polish)

Currently translated at 32.6% (80 of 245 strings)

Translated using Weblate (Ukrainian)

Currently translated at 87.6% (234 of 267 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.6% (266 of 267 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.6% (266 of 267 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (845 of 845 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Ukrainian)

Currently translated at 98.8% (425 of 430 strings)

Translated using Weblate (Ukrainian)

Currently translated at 57.6% (1946 of 3377 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 93.8% (107 of 114 strings)

Translated using Weblate (Ukrainian)

Currently translated at 57.5% (1944 of 3377 strings)

Translated using Weblate (Polish)

Currently translated at 66.8% (576 of 862 strings)

Translated using Weblate (Polish)

Currently translated at 59.3% (54 of 91 strings)

Translated using Weblate (Polish)

Currently translated at 89.8% (240 of 267 strings)

Translated using Weblate (Polish)

Currently translated at 87.4% (376 of 430 strings)

Translated using Weblate (Polish)

Currently translated at 52.3% (1769 of 3377 strings)

Translated using Weblate (Ukrainian)

Currently translated at 55.9% (137 of 245 strings)

Translated using Weblate (Italian)

Currently translated at 28.5% (70 of 245 strings)

Translated using Weblate (Polish)

Currently translated at 66.8% (576 of 862 strings)

Translated using Weblate (Polish)

Currently translated at 59.3% (54 of 91 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (412 of 412 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Polish)

Currently translated at 96.4% (110 of 114 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.6% (258 of 259 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 91.8% (395 of 430 strings)

Translated using Weblate (German)

Currently translated at 98.9% (3343 of 3377 strings)

Translated using Weblate (Ukrainian)

Currently translated at 55.5% (136 of 245 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 58.2% (53 of 91 strings)

Translated using Weblate (Korean)

Currently translated at 80.1% (730 of 911 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Polish)

Currently translated at 88.4% (229 of 259 strings)

Translated using Weblate (Polish)

Currently translated at 97.3% (401 of 412 strings)

Translated using Weblate (Polish)

Currently translated at 85.3% (221 of 259 strings)

Translated using Weblate (German)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (German)

Currently translated at 99.7% (909 of 911 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 87.5% (798 of 911 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Swedish)

Currently translated at 4.8% (12 of 245 strings)

Translated using Weblate (Swedish)

Currently translated at 1.2% (3 of 245 strings)

Translated using Weblate (Swedish)

Currently translated at 94.6% (53 of 56 strings)

Translated using Weblate (Swedish)

Currently translated at 77.6% (104 of 134 strings)

Translated using Weblate (Swedish)

Currently translated at 86.2% (50 of 58 strings)

Translated using Weblate (Swedish)

Currently translated at 66.5% (286 of 430 strings)

Translated using Weblate (Swedish)

Currently translated at 81.8% (199 of 243 strings)

Translated using Weblate (Swedish)

Currently translated at 51.6% (47 of 91 strings)

Translated using Weblate (Swedish)

Currently translated at 91.4% (43 of 47 strings)

Translated using Weblate (Swedish)

Currently translated at 66.2% (285 of 430 strings)

Translated using Weblate (Swedish)

Currently translated at 76.0% (140 of 184 strings)

Translated using Weblate (Swedish)

Currently translated at 53.6% (139 of 259 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Hebrew)

Currently translated at 78.9% (90 of 114 strings)

Translated using Weblate (Hebrew)

Currently translated at 66.4% (89 of 134 strings)

Translated using Weblate (Hebrew)

Currently translated at 56.2% (242 of 430 strings)

Translated using Weblate (Hebrew)

Currently translated at 38.5% (1301 of 3377 strings)

Translated using Weblate (Hebrew)

Currently translated at 90.5% (220 of 243 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Hebrew)

Currently translated at 66.1% (570 of 862 strings)

Translated using Weblate (Hebrew)

Currently translated at 98.4% (190 of 193 strings)

Translated using Weblate (Hebrew)

Currently translated at 58.6% (152 of 259 strings)

Translated using Weblate (Dutch)

Currently translated at 37.5% (92 of 245 strings)

Translated using Weblate (French)

Currently translated at 100.0% (259 of 259 strings)

Translated using Weblate (Dutch)

Currently translated at 37.1% (91 of 245 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (Hebrew)

Currently translated at 75.0% (6 of 8 strings)

Translated using Weblate (Hebrew)

Currently translated at 87.2% (212 of 243 strings)

Translated using Weblate (Hebrew)

Currently translated at 75.0% (138 of 184 strings)

Translated using Weblate (Hebrew)

Currently translated at 88.8% (48 of 54 strings)

Translated using Weblate (Hebrew)

Currently translated at 66.1% (570 of 862 strings)

Translated using Weblate (Hebrew)

Currently translated at 57.1% (52 of 91 strings)

Translated using Weblate (Hebrew)

Currently translated at 98.4% (190 of 193 strings)

Translated using Weblate (Hebrew)

Currently translated at 96.3% (106 of 110 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (3377 of 3377 strings)

Co-authored-by: Adrielle Marques <adrielle.marques3030@gmail.com>
Co-authored-by: Aleksander Mieciek <alex.mieciek@gmail.com>
Co-authored-by: AlexFad <2077505931@qq.com>
Co-authored-by: Alexandre Le Mercier <alexandre.le.mercier@ulb.be>
Co-authored-by: Ana <taranaana75@gmail.com>
Co-authored-by: Andrea <goffopaguro@gmail.com>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Eddy Nottingham <habitica.com.scone566@simplelogin.com>
Co-authored-by: FingerTiao <787170918@qq.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Jezz <lorraly@gmail.com>
Co-authored-by: Jezzica Israelsson <lorraly@gmail.com>
Co-authored-by: Jonathan Niessen <37.friedrich@gmail.com>
Co-authored-by: Karmelkowy <kicimeow.karmelio@gmail.com>
Co-authored-by: Laura Fleckenstein <fleckenstein_laura@web.de>
Co-authored-by: Maximiliano Guerra <guguloco2006@gmail.com>
Co-authored-by: Omer I.S <omeritzicschwartz@gmail.com>
Co-authored-by: Pasquale Bosso <protagora87@gmail.com>
Co-authored-by: Remigiusz Haziak <haziakremigiusz@gmail.com>
Co-authored-by: Ri Vargas <goldenhaitang@gmail.com>
Co-authored-by: Sara Olson <sara@habitica.com>
Co-authored-by: Sonia <sophishport@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Summer_GUI <heyang94@163.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Uwe B <hbtca@tunixgut.de>
Co-authored-by: Viktor Révész <rviktor@ivankapal.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: gongxueyan <1264752231@qq.com>
Co-authored-by: haru kake <hebey27020@bulmp3.com>
Co-authored-by: luckycccc <806009164@qq.com>
Co-authored-by: Ірина <ira.chipsa@gmail.com>
Co-authored-by: 吳昀錡 <J1120241@gm.fdhs.tyc.edu.tw>
Co-authored-by: 污染源 <polluter979@qq.com>
Co-authored-by: 김수연 <dus28232@naver.com>
Co-authored-by: ? <importantdata78@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es_419/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/he/
Translate-URL: https://translate.habitica.com/projects/habitica/character/de/
Translate-URL: https://translate.habitica.com/projects/habitica/character/he/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/he/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/content/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/content/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/death/he/
Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/he/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/it/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/front/de/
Translate-URL: https://translate.habitica.com/projects/habitica/front/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/front/he/
Translate-URL: https://translate.habitica.com/projects/habitica/front/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/front/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/he/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/he/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/he/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/he/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/he/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/he/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/he/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/he/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/it/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/es/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/he/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hans/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Character
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Contrib
Translation: Habitica/Death
Translation: Habitica/Defaulttasks
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Loginincentives
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Pets
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Settings
Translation: Habitica/Spells
Translation: Habitica/Subscriber
2025-07-21 11:58:35 +02:00
Fiz
068640311e Reload window after changing password (#15474)
* Reload window after changing password

* Shows password change success message

Displays a success snackbar after a user successfully changes their password. This provides visual confirmation to the user that the password update was successful.
The success message is displayed only once after the page reloads.

* lint fix trailing spaces
2025-07-16 13:07:28 -05:00
Fiz
f26d2a59ae add InvalidCredentialsError with language-agnostic code (#15472)
* add InvalidCredentialsError with language-agnostic code and update backend & web logout logic

* error.code in API error responses

Updated the error handler to serialize responseErr.code as the JSON error field, falling back to responseErr.name when no code is set.

* fix(lint): whitespace and missing def

* fix(lint): missed one

* add InvalidCredentialsError case for bad token

Add test verifying that auth middleware throws InvalidCredentialsError with code "invalid_credentials" and correct translated message when the API token is invalid.

* fix(test): user fields implicitly required

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-07-15 09:49:11 -05:00
Phillip Thelen
03c7e9172e fix double submit for pw reset (#15473) 2025-07-14 16:14:20 -05:00
negue
6fdc072ec3 reset the ApiToken on password changes/resets (#15433)
* reset the ApiToken on password changes/resets

* fix/add tests

* fix(typo): test grammar

* update new API Token Strings, removed unused one

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-07-01 12:30:34 -05:00
Kalista Payne
e68661c04b 5.37.1 2025-07-01 11:54:42 -05:00
Weblate
4f567592ea Translated using Weblate (Italian)
Currently translated at 84.6% (237 of 280 strings)

Translated using Weblate (Swedish)

Currently translated at 52.0% (139 of 267 strings)

Translated using Weblate (German)

Currently translated at 99.6% (266 of 267 strings)

Translated using Weblate (Swedish)

Currently translated at 94.6% (53 of 56 strings)

Translated using Weblate (Swedish)

Currently translated at 92.3% (12 of 13 strings)

Translated using Weblate (Swedish)

Currently translated at 86.8% (99 of 114 strings)

Translated using Weblate (Swedish)

Currently translated at 74.6% (100 of 134 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Swedish)

Currently translated at 66.0% (284 of 430 strings)

Translated using Weblate (Swedish)

Currently translated at 75.5% (139 of 184 strings)

Translated using Weblate (Swedish)

Currently translated at 72.0% (621 of 862 strings)

Translated using Weblate (Swedish)

Currently translated at 47.2% (43 of 91 strings)

Translated using Weblate (Swedish)

Currently translated at 87.2% (41 of 47 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (3377 of 3377 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3377 of 3377 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (3377 of 3377 strings)

Translated using Weblate (Spanish)

Currently translated at 99.9% (3375 of 3377 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 10.2% (25 of 245 strings)

Translated using Weblate (Spanish)

Currently translated at 99.8% (3372 of 3377 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 7.7% (19 of 245 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 94.6% (390 of 412 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 93.2% (125 of 134 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (French)

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (Spanish)

Currently translated at 99.7% (3369 of 3377 strings)

Translated using Weblate (German)

Currently translated at 98.9% (3341 of 3377 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 5.7% (14 of 245 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 83.8% (223 of 266 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 92.5% (124 of 134 strings)

Translated using Weblate (Portuguese)

Currently translated at 54.1% (1829 of 3377 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Portuguese)

Currently translated at 53.5% (1808 of 3377 strings)

Translated using Weblate (Italian)

Currently translated at 28.1% (69 of 245 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (3377 of 3377 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (412 of 412 strings)

Translated using Weblate (Polish)

Currently translated at 52.3% (1768 of 3377 strings)

Translated using Weblate (Polish)

Currently translated at 52.3% (1768 of 3377 strings)

Translated using Weblate (Polish)

Currently translated at 52.3% (1768 of 3377 strings)

Translated using Weblate (Polish)

Currently translated at 61.4% (172 of 280 strings)

Translated using Weblate (Polish)

Currently translated at 52.3% (1768 of 3377 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3377 of 3377 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Polish)

Currently translated at 32.2% (79 of 245 strings)

Translated using Weblate (Polish)

Currently translated at 94.4% (389 of 412 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (French)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Italian)

Currently translated at 92.3% (170 of 184 strings)

Translated using Weblate (Italian)

Currently translated at 27.3% (67 of 245 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (412 of 412 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Polish)

Currently translated at 99.1% (241 of 243 strings)

Translated using Weblate (Polish)

Currently translated at 96.2% (234 of 243 strings)

Translated using Weblate (Polish)

Currently translated at 96.2% (234 of 243 strings)

Translated using Weblate (Polish)

Currently translated at 86.5% (231 of 267 strings)

Translated using Weblate (Polish)

Currently translated at 94.6% (89 of 94 strings)

Translated using Weblate (Polish)

Currently translated at 91.3% (222 of 243 strings)

Translated using Weblate (Polish)

Currently translated at 30.6% (75 of 245 strings)

Translated using Weblate (Polish)

Currently translated at 30.6% (75 of 245 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Polish)

Currently translated at 84.2% (225 of 267 strings)

Translated using Weblate (Polish)

Currently translated at 84.2% (225 of 267 strings)

Translated using Weblate (Polish)

Currently translated at 84.2% (225 of 267 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (243 of 243 strings)

Translated using Weblate (Russian)

Currently translated at 99.1% (241 of 243 strings)

Translated using Weblate (Russian)

Currently translated at 99.1% (241 of 243 strings)

Translated using Weblate (Russian)

Currently translated at 96.2% (234 of 243 strings)

Translated using Weblate (Russian)

Currently translated at 96.2% (234 of 243 strings)

Translated using Weblate (Russian)

Currently translated at 93.4% (227 of 243 strings)

Translated using Weblate (Russian)

Currently translated at 93.4% (227 of 243 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (Russian)

Currently translated at 88.8% (2992 of 3367 strings)

Translated using Weblate (Russian)

Currently translated at 88.8% (2992 of 3367 strings)

Translated using Weblate (Russian)

Currently translated at 88.8% (2992 of 3367 strings)

Translated using Weblate (Italian)

Currently translated at 86.3% (2907 of 3367 strings)

Translated using Weblate (Russian)

Currently translated at 71.8% (176 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 70.2% (172 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Russian)

Currently translated at 66.9% (164 of 245 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (911 of 911 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (134 of 134 strings)

Translated using Weblate (Italian)

Currently translated at 22.0% (54 of 245 strings)

Translated using Weblate (Italian)

Currently translated at 22.0% (54 of 245 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (110 of 110 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (908 of 908 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (908 of 908 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (167 of 167 strings)

Co-authored-by: Bartosz Babik <kotka-wali0h@icloud.com>
Co-authored-by: Goldy <mariesipova314@gmail.com>
Co-authored-by: Jackal <qwerty70244@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: Jan P <jankrzpow@gmail.com>
Co-authored-by: Jezzica Israelsson <lorraly@gmail.com>
Co-authored-by: Jonathan Niessen <37.friedrich@gmail.com>
Co-authored-by: Karmelkowy <kicimeow.karmelio@gmail.com>
Co-authored-by: Kernis <kerhsing.wang@gmail.com>
Co-authored-by: LaiYi <lysinexxin@163.com>
Co-authored-by: Nicolas Samuel Reuter <nicolasreuter1@gmail.com>
Co-authored-by: Nik <doni.della.morte5619@gmail.com>
Co-authored-by: Pasquale Bosso <protagora87@gmail.com>
Co-authored-by: R. J <ricardo@pinho.org>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Sugo Gangotti <giacomo@ergonomia.it>
Co-authored-by: Summer_GUI <heyang94@163.com>
Co-authored-by: Svetlana <shkulepo@rambler.ru>
Co-authored-by: Tetiana <merekka13@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Val <3qes0hnzh@mozmail.com>
Co-authored-by: Viktor Révész <rviktor@ivankapal.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Zuz Q <zuzannakunik@gmail.com>
Co-authored-by: fitria nabila <fitria3nabila@gmail.com>
Co-authored-by: konhi <hello.konhi@gmail.com>
Co-authored-by: razil <boss.razmarin@gmail.com>
Co-authored-by: 吳昀錡 <J1120241@gm.fdhs.tyc.edu.tw>
Co-authored-by: ? <importantdata78@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/it/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/id/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/it/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/it/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/character/it/
Translate-URL: https://translate.habitica.com/projects/habitica/character/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/it/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/content/it/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/content/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/content/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/it/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/it/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/front/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/front/it/
Translate-URL: https://translate.habitica.com/projects/habitica/front/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/front/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/it/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/it/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/it/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/it/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/de/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/sv/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hant/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Character
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Contrib
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Loginincentives
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Pets
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Rebirth
Translation: Habitica/Spells
Translation: Habitica/Subscriber
2025-07-01 05:18:55 +02:00
Kalista Payne
63c9b7a894 fix(groups): add missing markModified 2025-06-26 18:08:57 -05:00
Kalista Payne
eaec39188e chore(subproj): update habitica-images 2025-06-26 17:55:24 -05:00
Natalie
ba6940eb81 chore: update time travelers shop to display seasonal backgrounds (#15445)
* chore: update time travelers shop to display seasonal backgrounds

* chore: update time travelers banner (note CSS borken rn)

* chore: fix borken CSS and update logic in shop

* chore: added isSubscribed function, not working

* chore: isSubscribed working but no bg for subscribers

* chore: logic and css updates

* chore: add check for trinket

* chore: more time traveler shop logicking

* chore(git): heckin habitica-images

* refactor(style): indents/readability

* refactor(style): one more line break

* refactor(style): still more indents

* refactor(style): I wonder if lint can help with this stuff

* refactor(style): tighten up

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-06-25 15:12:23 -05:00
Kalista Payne
f8a3e4d673 perf(groups): use simplest possible query for cron script (#15457) 2025-06-25 14:56:50 -05:00
Phillip Thelen
2727da6f6c Correctly define components (#15458) 2025-06-25 14:56:29 -05:00
Kalista Payne
fa97852e38 5.37.0 2025-06-21 08:57:40 -05:00
Weblate
2c7da25a25 Translated using Weblate (Dutch)
Currently translated at 35.1% (86 of 245 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (French)

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 79.6% (212 of 266 strings)

Translated using Weblate (French)

Currently translated at 100.0% (184 of 184 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.5% (406 of 412 strings)

Translated using Weblate (French)

Currently translated at 96.1% (177 of 184 strings)

Co-authored-by: Alexandre Le Mercier <alexandre.le.mercier@ulb.be>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: LaiYi <lysinexxin@163.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/front/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/es/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hant/
Translation: Habitica/Content
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Subscriber
2025-06-21 15:07:30 +02:00
Natalie
9a072e3e76 chore: update grand gala start dates (#15435)
* chore: update grand gala start date

* chore: update test cases for new gala dates

* chore(sales): adjust promo dates

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-06-19 18:05:38 -05:00
Natalie
823b339d27 chore: July 2025 content build (#15449)
* chore: July 2025 content build

* chore: add release dates to armoire

* fix(sprites): correct Cryptid typo

* fix(grammar): missing comma

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-06-17 13:34:57 -05:00
Kalista Payne
98a6535dc3 fix(content): don't filter out the thing we want 2025-06-16 16:43:56 -05:00
Kalista Payne
9948e8ee44 fix(mobile): provide Challenge categories via API 2025-06-16 16:43:24 -05:00
313 changed files with 3955 additions and 1663 deletions

76
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "habitica", "name": "habitica",
"version": "5.36.6", "version": "5.38.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "habitica", "name": "habitica",
"version": "5.36.6", "version": "5.38.2",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@babel/core": "^7.22.10", "@babel/core": "^7.22.10",
@@ -59,7 +59,6 @@
"morgan": "^1.10.0", "morgan": "^1.10.0",
"nconf": "^0.12.1", "nconf": "^0.12.1",
"node-gcm": "^1.0.5", "node-gcm": "^1.0.5",
"nodemon": "^3.1.9",
"on-headers": "^1.0.2", "on-headers": "^1.0.2",
"passport": "^0.5.3", "passport": "^0.5.3",
"passport-facebook": "^3.0.0", "passport-facebook": "^3.0.0",
@@ -16011,55 +16010,6 @@
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw=="
}, },
"node_modules/nodemon": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz",
"integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==",
"dependencies": {
"chokidar": "^3.5.2",
"debug": "^4",
"ignore-by-default": "^1.0.1",
"minimatch": "^3.1.2",
"pstree.remy": "^1.1.8",
"semver": "^7.5.3",
"simple-update-notifier": "^2.0.0",
"supports-color": "^5.5.0",
"touch": "^3.1.0",
"undefsafe": "^2.0.5"
},
"bin": {
"nodemon": "bin/nodemon.js"
},
"engines": {
"node": ">=10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/nodemon"
}
},
"node_modules/nodemon/node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/nodemon/node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/noop-logger": { "node_modules/noop-logger": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
@@ -19423,28 +19373,6 @@
"is-arrayish": "^0.3.1" "is-arrayish": "^0.3.1"
} }
}, },
"node_modules/simple-update-notifier": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
"integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
"dependencies": {
"semver": "^7.5.3"
},
"engines": {
"node": ">=10"
}
},
"node_modules/simple-update-notifier/node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/sinon": { "node_modules/sinon": {
"version": "15.2.0", "version": "15.2.0",
"resolved": "https://registry.npmjs.org/sinon/-/sinon-15.2.0.tgz", "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.2.0.tgz",

View File

@@ -1,7 +1,7 @@
{ {
"name": "habitica", "name": "habitica",
"description": "A habit tracker app which treats your goals like a Role Playing Game.", "description": "A habit tracker app which treats your goals like a Role Playing Game.",
"version": "5.36.6", "version": "5.38.2",
"main": "./website/server/index.js", "main": "./website/server/index.js",
"dependencies": { "dependencies": {
"@babel/core": "^7.22.10", "@babel/core": "^7.22.10",
@@ -106,8 +106,8 @@
"start": "node --watch ./website/server/index.js", "start": "node --watch ./website/server/index.js",
"start:simple": "node ./website/server/index.js", "start:simple": "node ./website/server/index.js",
"debug": "node --watch --inspect ./website/server/index.js", "debug": "node --watch --inspect ./website/server/index.js",
"mongo:dev": "run-rs -v 5.0.23 -l ubuntu1804 --keep --dbpath mongodb-data --number 1 --quiet", "mongo:dev": "run-rs -v 7.0.23 -l ubuntu2404 --keep --dbpath mongodb-data --number 1 --quiet",
"mongo:test": "run-rs -v 5.0.23 -l ubuntu1804 --keep --dbpath mongodb-data-testing --number 1 --quiet", "mongo:test": "run-rs -v 7.0.23 -l ubuntu2404 --keep --dbpath mongodb-data-testing --number 1 --quiet",
"postinstall": "git config --global url.\"https://\".insteadOf git:// && gulp build && cd website/client && npm install", "postinstall": "git config --global url.\"https://\".insteadOf git:// && gulp build && cd website/client && npm install",
"apidoc": "gulp apidoc", "apidoc": "gulp apidoc",
"heroku-postbuild": ".heroku/report_deploy.sh" "heroku-postbuild": ".heroku/report_deploy.sh"

View File

@@ -8,7 +8,17 @@ const TASK_VALUE_CHANGE_FACTOR = 0.9747;
const MIN_TASK_VALUE = -47.27; const MIN_TASK_VALUE = -47.27;
async function updateTeamTasks (team) { async function updateTeamTasks (team) {
if (team.purchased.plan.dateTerminated) {
const dateTerminated = new Date(team.purchased.plan.dateTerminated);
if (dateTerminated < new Date()) {
team.purchased.plan.customerId = undefined;
team.markModified('purchased.plan');
return team.save();
}
}
const toSave = []; const toSave = [];
let teamLeader = await User.findOne({ _id: team.leader }, 'preferences').exec(); let teamLeader = await User.findOne({ _id: team.leader }, 'preferences').exec();
if (!teamLeader) { // why would this happen? if (!teamLeader) { // why would this happen?
@@ -93,12 +103,7 @@ async function updateTeamTasks (team) {
export default async function processTeamsCron () { export default async function processTeamsCron () {
const activeTeams = await Group.find({ const activeTeams = await Group.find({
'purchased.plan.customerId': { $exists: true }, 'purchased.plan.customerId': { $exists: true },
$or: [ }, { cron: 1, leader: 1, purchased: 1 }).exec();
{ 'purchased.plan.dateTerminated': { $exists: false } },
{ 'purchased.plan.dateTerminated': null },
{ 'purchased.plan.dateTerminated': { $gt: new Date() } },
],
}).exec();
const cronPromises = activeTeams.map(updateTeamTasks); const cronPromises = activeTeams.map(updateTeamTasks);
return Promise.all(cronPromises); return Promise.all(cronPromises);

View File

@@ -1,8 +1,11 @@
import nconf from 'nconf';
import requireAgain from 'require-again';
import { import {
generateRes, generateRes,
generateReq, generateReq,
} from '../../../helpers/api-unit.helper'; } from '../../../helpers/api-unit.helper';
import { authWithHeaders as authWithHeadersFactory } from '../../../../website/server/middlewares/auth';
const authPath = '../../../../website/server/middlewares/auth';
describe('auth middleware', () => { describe('auth middleware', () => {
let res; let req; let let res; let req; let
@@ -16,6 +19,7 @@ describe('auth middleware', () => {
describe('auth with headers', () => { describe('auth with headers', () => {
it('allows to specify a list of user field that we do not want to load', done => { it('allows to specify a list of user field that we do not want to load', done => {
const authWithHeadersFactory = requireAgain(authPath).authWithHeaders;
const authWithHeaders = authWithHeadersFactory({ const authWithHeaders = authWithHeadersFactory({
userFieldsToExclude: ['items'], userFieldsToExclude: ['items'],
}); });
@@ -35,6 +39,7 @@ describe('auth middleware', () => {
}); });
it('makes sure some fields are always included', done => { it('makes sure some fields are always included', done => {
const authWithHeadersFactory = requireAgain(authPath).authWithHeaders;
const authWithHeaders = authWithHeadersFactory({ const authWithHeaders = authWithHeadersFactory({
userFieldsToExclude: [ userFieldsToExclude: [
'items', 'auth.timestamps', 'items', 'auth.timestamps',
@@ -60,5 +65,57 @@ describe('auth middleware', () => {
return done(); return done();
}); });
}); });
it('errors with InvalidCredentialsError and code when token is wrong', done => {
const authWithHeadersFactory = requireAgain(authPath).authWithHeaders;
const authWithHeaders = authWithHeadersFactory({ userFieldsToExclude: [] });
req.headers['x-api-user'] = user._id;
req.headers['x-api-key'] = 'totally-wrong-token';
authWithHeaders(req, res, err => {
expect(err).to.exist;
expect(err.name).to.equal('InvalidCredentialsError');
expect(err.code).to.equal('invalid_credentials');
expect(err.message).to.equal(res.t('invalidCredentials'));
return done();
});
});
describe('when ENFORCE_CLIENT_HEADER is true', () => {
let authFactory;
beforeEach(() => {
sandbox.stub(nconf, 'get').withArgs('ENFORCE_CLIENT_HEADER').returns('true');
authFactory = requireAgain(authPath).authWithHeaders;
});
it('errors with missingClientHeader when x-client header is not present', done => {
const authWithHeaders = authFactory({ userFieldsToExclude: [] });
req.headers['x-api-user'] = user._id;
req.headers['x-api-key'] = user;
authWithHeaders(req, res, err => {
expect(err).to.exist;
expect(err.name).to.equal('BadRequest');
expect(err.message).to.equal(res.t('missingClientHeader'));
return done();
});
});
it('allows request to pass when x-client header is present', done => {
const authWithHeaders = authFactory({ userFieldsToExclude: [] });
req.headers['x-api-user'] = user._id;
req.headers['x-api-key'] = user.apiToken;
req.headers['x-client'] = 'habitica-web';
authWithHeaders(req, res, err => {
if (err) return done(err);
expect(res.locals.user).to.exist;
return done();
});
});
});
}); });
}); });

View File

@@ -0,0 +1,206 @@
import nconf from 'nconf';
import requireAgain from 'require-again';
import {
generateRes,
generateReq,
generateNext,
} from '../../../helpers/api-unit.helper';
import { Forbidden } from '../../../../website/server/libs/errors';
import { apiError } from '../../../../website/server/libs/apiError';
import { model as Blocker } from '../../../../website/server/models/blocker';
function checkIPBlockedErrorThrown (next) {
expect(next).to.have.been.calledOnce;
const calledWith = next.getCall(0).args;
expect(calledWith[0].message).to.equal(apiError('ipAddressBlocked'));
expect(calledWith[0] instanceof Forbidden).to.equal(true);
}
function checkClientBlockedErrorThrown (next) {
expect(next).to.have.been.calledOnce;
const calledWith = next.getCall(0).args;
expect(calledWith[0].message).to.equal(apiError('clientBlocked'));
expect(calledWith[0] instanceof Forbidden).to.equal(true);
}
function checkErrorNotThrown (next) {
expect(next).to.have.been.calledOnce;
const calledWith = next.getCall(0).args;
expect(typeof calledWith[0] === 'undefined').to.equal(true);
}
describe('Blocker middleware', () => {
const pathToBlocker = '../../../../website/server/middlewares/blocker';
let res; let req; let next;
beforeEach(() => {
res = generateRes();
req = generateReq();
next = generateNext();
});
describe('Blocking IPs', () => {
it('is disabled when the env var is not defined', () => {
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns(undefined);
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('is disabled when the env var is an empty string', () => {
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('');
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('is disabled when the env var contains comma separated empty strings', () => {
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns(' , , ');
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('does not throw when the ip does not match', () => {
req.ip = '192.168.1.1';
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('192.168.1.2');
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('does not throw when the blocker IP does not match', async () => {
req.ip = '192.168.1.1';
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
if (event === 'change') {
callback({ operation: 'add', blocker: { type: 'ipaddress', area: 'full', value: '192.168.1.2' } });
}
},
});
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('does not throw when a client is blocked', async () => {
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
if (event === 'change') {
callback({ operation: 'add', blocker: { type: 'client', area: 'full', value: '192.168.1.1' } });
}
},
});
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('throws when the blocker IP is blocked', async () => {
req.ip = '192.168.1.1';
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
if (event === 'change') {
callback({ operation: 'add', blocker: { type: 'ipaddress', area: 'full', value: '192.168.1.1' } });
}
},
});
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkIPBlockedErrorThrown(next);
});
it('throws when the ip is blocked', () => {
req.ip = '192.168.1.1';
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('192.168.1.1');
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkIPBlockedErrorThrown(next);
});
});
describe('Blocking clients', () => {
beforeEach(() => {
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('');
req.headers['x-client'] = 'test-client';
});
it('is disabled when no clients are blocked', () => {
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('does not throw when the client does not match', async () => {
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
if (event === 'change') {
callback({ operation: 'add', blocker: { type: 'client', area: 'full', value: 'another-client' } });
}
},
});
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('throws when the client is blocked', async () => {
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
if (event === 'change') {
callback({ operation: 'add', blocker: { type: 'client', area: 'full', value: 'test-client' } });
}
},
});
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkClientBlockedErrorThrown(next);
});
it('does not throw when an ip is blocked', async () => {
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
if (event === 'change') {
callback({ operation: 'add', blocker: { type: 'ipaddress', area: 'full', value: 'test-client' } });
}
},
});
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('updates the list when data changes', async () => {
let blockCallback;
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
blockCallback = callback;
if (event === 'change') {
callback({ operation: 'add', blocker: { type: 'client', area: 'full', value: 'another-client' } });
}
},
});
const attachBlocker = requireAgain(pathToBlocker).default;
attachBlocker(req, res, next);
checkErrorNotThrown(next);
blockCallback({ operation: 'add', blocker: { type: 'client', area: 'full', value: 'test-client' } });
attachBlocker(req, res, next);
expect(next).to.have.been.calledTwice;
const calledWith = next.getCall(1).args;
expect(calledWith[0].message).to.equal(apiError('clientBlocked'));
expect(calledWith[0] instanceof Forbidden).to.equal(true);
});
});
});

View File

@@ -1,76 +0,0 @@
import nconf from 'nconf';
import requireAgain from 'require-again';
import {
generateRes,
generateReq,
generateNext,
} from '../../../helpers/api-unit.helper';
import { Forbidden } from '../../../../website/server/libs/errors';
import { apiError } from '../../../../website/server/libs/apiError';
function checkErrorThrown (next) {
expect(next).to.have.been.calledOnce;
const calledWith = next.getCall(0).args;
expect(calledWith[0].message).to.equal(apiError('ipAddressBlocked'));
expect(calledWith[0] instanceof Forbidden).to.equal(true);
}
function checkErrorNotThrown (next) {
expect(next).to.have.been.calledOnce;
const calledWith = next.getCall(0).args;
expect(typeof calledWith[0] === 'undefined').to.equal(true);
}
describe('ipBlocker middleware', () => {
const pathToIpBlocker = '../../../../website/server/middlewares/ipBlocker';
let res; let req; let next;
beforeEach(() => {
res = generateRes();
req = generateReq();
next = generateNext();
});
it('is disabled when the env var is not defined', () => {
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns(undefined);
const attachIpBlocker = requireAgain(pathToIpBlocker).default;
attachIpBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('is disabled when the env var is an empty string', () => {
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('');
const attachIpBlocker = requireAgain(pathToIpBlocker).default;
attachIpBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('is disabled when the env var contains comma separated empty strings', () => {
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns(' , , ');
const attachIpBlocker = requireAgain(pathToIpBlocker).default;
attachIpBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('does not throw when the ip does not match', () => {
req.ip = '192.168.1.1';
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('192.168.1.2');
const attachIpBlocker = requireAgain(pathToIpBlocker).default;
attachIpBlocker(req, res, next);
checkErrorNotThrown(next);
});
it('throws when the ip is blocked', () => {
req.ip = '192.168.1.1';
sandbox.stub(nconf, 'get').withArgs('BLOCKED_IPS').returns('192.168.1.1');
const attachIpBlocker = requireAgain(pathToIpBlocker).default;
attachIpBlocker(req, res, next);
checkErrorThrown(next);
});
});

View File

@@ -1,9 +1,13 @@
import moment from 'moment'; import moment from 'moment';
import requireAgain from 'require-again';
import { model as User } from '../../../../website/server/models/user'; import { model as User } from '../../../../website/server/models/user';
import { model as NewsPost } from '../../../../website/server/models/newsPost'; import { model as NewsPost } from '../../../../website/server/models/newsPost';
import { model as Group } from '../../../../website/server/models/group'; import { model as Group } from '../../../../website/server/models/group';
import { model as Blocker } from '../../../../website/server/models/blocker';
import common from '../../../../website/common'; import common from '../../../../website/common';
const pathToUserSchema = '../../../../website/server/models/user/schema';
describe('User Model', () => { describe('User Model', () => {
describe('.toJSON()', () => { describe('.toJSON()', () => {
it('keeps user._tmp when calling .toJSON', () => { it('keeps user._tmp when calling .toJSON', () => {
@@ -912,4 +916,73 @@ describe('User Model', () => {
expect(user.toJSON().flags.newStuff).to.equal(true); expect(user.toJSON().flags.newStuff).to.equal(true);
}); });
}); });
describe('validates email', () => {
it('does not throw an error for a valid email', () => {
const user = new User();
user.auth.local.email = 'hello@example.com';
const errors = user.validateSync();
expect(errors.errors['auth.local.email']).to.not.exist;
});
it('throws an error if email is not valid', () => {
const user = new User();
user.auth.local.email = 'invalid-email';
const errors = user.validateSync();
expect(errors.errors['auth.local.email'].message).to.equal(common.i18n.t('invalidEmail'));
});
it('throws an error if email is using a restricted domain', () => {
const user = new User();
user.auth.local.email = 'scammer@habitica.com';
const errors = user.validateSync();
expect(errors.errors['auth.local.email'].message).to.equal(common.i18n.t('invalidEmailDomain', { domains: 'habitica.com, habitrpg.com' }));
});
it('throws an error if email was blocked specifically', () => {
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
callback({ operation: 'add', blocker: { type: 'email', area: 'full', value: 'blocked@example.com' } });
},
});
const schema = requireAgain(pathToUserSchema).UserSchema;
const valid = schema.paths['auth.local.email'].options.validate.every(v => v.validator('blocked@example.com'));
expect(valid).to.equal(false);
});
it('throws an error if email domain was blocked', () => {
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
callback({ operation: 'add', blocker: { type: 'email', area: 'full', value: '@example.com' } });
},
});
const schema = requireAgain(pathToUserSchema).UserSchema;
const valid = schema.paths['auth.local.email'].options.validate.every(v => v.validator('blocked@example.com'));
expect(valid).to.equal(false);
});
it('throws an error if user portion of email was blocked', () => {
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
callback({ operation: 'add', blocker: { type: 'email', area: 'full', value: 'blocked@' } });
},
});
const schema = requireAgain(pathToUserSchema).UserSchema;
const valid = schema.paths['auth.local.email'].options.validate.every(v => v.validator('blocked@example.com'));
expect(valid).to.equal(false);
});
it('does not throw an error if email is not blocked', () => {
sandbox.stub(Blocker, 'watchBlockers').returns({
on: (event, callback) => {
callback({ operation: 'add', blocker: { type: 'email', area: 'full', value: '@example.com' } });
callback({ operation: 'add', blocker: { type: 'email', area: 'full', value: 'blocked@' } });
callback({ operation: 'add', blocker: { type: 'email', area: 'full', value: 'bad@test.com' } });
},
});
const schema = requireAgain(pathToUserSchema).UserSchema;
const valid = schema.paths['auth.local.email'].options.validate.every(v => v.validator('good@test.com'));
expect(valid).to.equal(true);
});
});
}); });

View File

@@ -238,6 +238,28 @@ describe('POST /user/auth/reset-password-set-new-one', () => {
expect(isPassValid).to.equal(true); expect(isPassValid).to.equal(true);
}); });
it('changes the apiToken on password reset', async () => {
const user = await generateUser();
const previousToken = user.apiToken;
const code = encrypt(JSON.stringify({
userId: user._id,
expiresAt: moment().add({ days: 1 }),
}));
await user.updateOne({
'auth.local.passwordResetCode': code,
});
await api.post(`${endpoint}`, {
newPassword: 'my new password',
confirmPassword: 'my new password',
code,
});
await user.sync();
expect(user.apiToken).to.not.eql(previousToken);
});
it('renders the success page and convert the password from sha1 to bcrypt', async () => { it('renders the success page and convert the password from sha1 to bcrypt', async () => {
const user = await generateUser(); const user = await generateUser();

View File

@@ -44,7 +44,7 @@ describe('POST /user/auth/local/login', () => {
})).to.eventually.be.rejected.and.eql({ })).to.eventually.be.rejected.and.eql({
code: 401, code: 401,
error: 'NotAuthorized', error: 'NotAuthorized',
message: t('accountSuspended', { communityManagerEmail: nconf.get('EMAILS_COMMUNITY_MANAGER_EMAIL'), userId: user._id }), message: t('accountSuspended', { communityManagerEmail: nconf.get('EMAILS_COMMUNITY_MANAGER_EMAIL'), userId: user._id, username: user.auth.local.username }),
}); });
}); });

View File

@@ -27,11 +27,30 @@ describe('PUT /user/auth/update-password', async () => {
newPassword, newPassword,
confirmPassword: newPassword, confirmPassword: newPassword,
}); });
expect(response).to.eql({});
expect(response).to.exist;
expect(response.apiToken).to.exist;
await user.sync(); await user.sync();
expect(user.auth.local.hashed_password).to.not.eql(previousHashedPassword); expect(user.auth.local.hashed_password).to.not.eql(previousHashedPassword);
}); });
it('should change the apiToken on password change', async () => {
const previousToken = user.apiToken;
const response = await user.put(ENDPOINT, {
password,
newPassword,
confirmPassword: newPassword,
});
const newToken = response.apiToken;
expect(newToken).to.exist;
await user.sync();
expect(user.apiToken).to.eql(newToken);
expect(user.apiToken).to.not.eql(previousToken);
});
it('returns an error when confirmPassword does not match newPassword', async () => { it('returns an error when confirmPassword does not match newPassword', async () => {
await expect(user.put(ENDPOINT, { await expect(user.put(ENDPOINT, {
password, password,

View File

@@ -133,21 +133,21 @@ describe('Content Schedule', () => {
}); });
it('sets the end date for a gala', () => { it('sets the end date for a gala', () => {
const date = new Date('2024-05-20'); const date = new Date('2024-05-31');
const matchers = getAllScheduleMatchingGroups(date); const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.seasonalGear.end).to.eql(moment.utc(`2024-06-21T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate()); expect(matchers.seasonalGear.end).to.eql(moment.utc(`2024-06-01T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
}); });
it('sets the end date for a winter gala', () => { it('sets the end date for a winter gala', () => {
const date = new Date('2024-12-22'); const date = new Date('2025-02-28');
const matchers = getAllScheduleMatchingGroups(date); const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.seasonalGear.end).to.eql(moment.utc(`2025-03-21T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate()); expect(matchers.seasonalGear.end).to.eql(moment.utc(`2025-03-01T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
}); });
it('sets the end date in new year for a winter gala', () => { it('sets the end date in new year for a winter gala', () => {
const date = new Date('2025-01-04'); const date = new Date('2025-02-28');
const matchers = getAllScheduleMatchingGroups(date); const matchers = getAllScheduleMatchingGroups(date);
expect(matchers.seasonalGear.end).to.eql(moment.utc(`2025-03-21T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate()); expect(matchers.seasonalGear.end).to.eql(moment.utc(`2025-03-01T${String(switchoverTime).padStart(2, '0')}:00:00.000Z`).toDate());
}); });
it('uses correct date for first hours of the month', () => { it('uses correct date for first hours of the month', () => {

View File

@@ -111,6 +111,7 @@ import axios from 'axios';
import * as Analytics from '@/libs/analytics'; import * as Analytics from '@/libs/analytics';
import { mapState } from '@/libs/store'; import { mapState } from '@/libs/store';
import snackbars from '@/components/snackbars/notifications'; import snackbars from '@/components/snackbars/notifications';
import { LOCALSTORAGE_AUTH_KEY } from '@/libs/auth';
const COMMUNITY_MANAGER_EMAIL = import.meta.env.EMAILS_COMMUNITY_MANAGER_EMAIL; const COMMUNITY_MANAGER_EMAIL = import.meta.env.EMAILS_COMMUNITY_MANAGER_EMAIL;
@@ -222,11 +223,10 @@ export default {
const errorData = error.response.data; const errorData = error.response.data;
const errorMessage = errorData.message || errorData; const errorMessage = errorData.message || errorData;
const errorCode = errorData.error;
// Check for conditions to reset the user auth // If 'invalid_credentials' signaled, force logout
// TODO use a specific error like NotificationNotFound instead of checking for the string if (error.response.status === 401 && errorCode === 'invalid_credentials') {
const invalidUserMessage = [this.$t('invalidCredentials'), 'Missing authentication headers.'];
if (invalidUserMessage.indexOf(errorMessage) !== -1) {
this.$store.dispatch('auth:logout', { redirectToLogin: true }); this.$store.dispatch('auth:logout', { redirectToLogin: true });
return null; return null;
} }
@@ -269,6 +269,17 @@ export default {
const loadingScreen = document.getElementById('loading-screen'); const loadingScreen = document.getElementById('loading-screen');
if (loadingScreen) document.body.removeChild(loadingScreen); if (loadingScreen) document.body.removeChild(loadingScreen);
// Check if we need to show password change success message
if (sessionStorage.getItem('passwordChangeSuccess') === 'true') {
sessionStorage.removeItem('passwordChangeSuccess');
this.$store.dispatch('snackbars:add', {
title: 'Habitica',
text: this.$t('passwordSuccess'),
type: 'success',
timeout: true,
});
}
this.$router.onReady(() => { this.$router.onReady(() => {
if (this.isStaticPage || !this.isUserLoggedIn) { if (this.isStaticPage || !this.isUserLoggedIn) {
this.hideLoadingScreen(); this.hideLoadingScreen();
@@ -280,7 +291,7 @@ export default {
this.loading = false; this.loading = false;
}, },
checkForBannedUser (error) { checkForBannedUser (error) {
const AUTH_SETTINGS = localStorage.getItem('habit-mobile-settings'); const AUTH_SETTINGS = localStorage.getItem(LOCALSTORAGE_AUTH_KEY);
const parseSettings = JSON.parse(AUTH_SETTINGS); const parseSettings = JSON.parse(AUTH_SETTINGS);
const errorMessage = error.response.data.message; const errorMessage = error.response.data.message;

View File

@@ -177,7 +177,7 @@
height: 96px; height: 96px;
} }
.Mount_Head_Gryphon-Gryphatrice, .Mount_Body_Gryphon-Gryphatrice { .Mount_Head_Gryphon-Gryphatrice, .Mount_Body_Gryphon-Gryphatrice, .Mount_Head_Dragon-Hydra, .Mount_Body_Dragon-Hydra {
width: 135px; width: 135px;
height: 135px; height: 135px;
} }
@@ -190,6 +190,14 @@
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Mount-Body-Gryphatrice.gif") no-repeat; background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/BackerOnly-Mount-Body-Gryphatrice.gif") no-repeat;
} }
.Mount_Head_Dragon-Hydra {
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Mount_Head_Dragon-Hydra.gif") no-repeat;
}
.Mount_Body_Dragon-Hydra {
background: url("https://habitica-assets.s3.amazonaws.com/mobileApp/images/Mount_Body_Dragon-Hydra.gif") no-repeat;
}
.background_airship, .background_clocktower, .background_steamworks { .background_airship, .background_clocktower, .background_steamworks {
width: 141px; width: 141px;
height: 147px; height: 147px;

View File

@@ -2001,6 +2001,11 @@
width: 141px; width: 141px;
height: 147px; height: 147px;
} }
.background_sirens_lair {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_sirens_lair.png');
width: 141px;
height: 147px;
}
.background_slimy_swamp { .background_slimy_swamp {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_slimy_swamp.png'); background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_slimy_swamp.png');
width: 141px; width: 141px;
@@ -2186,6 +2191,11 @@
width: 141px; width: 141px;
height: 147px; height: 147px;
} }
.background_sunny_street_with_shops {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_sunny_street_with_shops.png');
width: 141px;
height: 147px;
}
.background_sunset_meadow { .background_sunset_meadow {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_sunset_meadow.png'); background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_sunset_meadow.png');
width: 141px; width: 141px;
@@ -29685,6 +29695,11 @@
width: 114px; width: 114px;
height: 90px; height: 90px;
} }
.broad_armor_armoire_flyFishingWaders {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_armoire_flyFishingWaders.png');
width: 114px;
height: 90px;
}
.broad_armor_armoire_funnyFoolCostume { .broad_armor_armoire_funnyFoolCostume {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_armoire_funnyFoolCostume.png'); background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_armoire_funnyFoolCostume.png');
width: 114px; width: 114px;
@@ -29890,6 +29905,11 @@
width: 90px; width: 90px;
height: 90px; height: 90px;
} }
.broad_armor_armoire_redWaistcoat {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_armoire_redWaistcoat.png');
width: 114px;
height: 90px;
}
.broad_armor_armoire_robeOfDiamonds { .broad_armor_armoire_robeOfDiamonds {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_armoire_robeOfDiamonds.png'); background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_armoire_robeOfDiamonds.png');
width: 114px; width: 114px;
@@ -30300,6 +30320,11 @@
width: 114px; width: 114px;
height: 90px; height: 90px;
} }
.head_armoire_flyFishingHat {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_flyFishingHat.png');
width: 114px;
height: 90px;
}
.head_armoire_frostedHelm { .head_armoire_frostedHelm {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_frostedHelm.png'); background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_frostedHelm.png');
width: 114px; width: 114px;
@@ -30520,6 +30545,11 @@
width: 90px; width: 90px;
height: 90px; height: 90px;
} }
.head_armoire_redNewsieHat {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_redNewsieHat.png');
width: 114px;
height: 90px;
}
.head_armoire_regalCrown { .head_armoire_regalCrown {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_regalCrown.png'); background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_regalCrown.png');
width: 114px; width: 114px;
@@ -30765,6 +30795,11 @@
width: 114px; width: 114px;
height: 90px; height: 90px;
} }
.shield_armoire_flyFishingRod {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_armoire_flyFishingRod.png');
width: 114px;
height: 90px;
}
.shield_armoire_gardenersSpade { .shield_armoire_gardenersSpade {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_armoire_gardenersSpade.png'); background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_armoire_gardenersSpade.png');
width: 114px; width: 114px;
@@ -31245,6 +31280,11 @@
width: 114px; width: 114px;
height: 90px; height: 90px;
} }
.slim_armor_armoire_flyFishingWaders {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_flyFishingWaders.png');
width: 114px;
height: 90px;
}
.slim_armor_armoire_funnyFoolCostume { .slim_armor_armoire_funnyFoolCostume {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_funnyFoolCostume.png'); background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_funnyFoolCostume.png');
width: 114px; width: 114px;
@@ -31450,6 +31490,11 @@
width: 90px; width: 90px;
height: 90px; height: 90px;
} }
.slim_armor_armoire_redWaistcoat {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_redWaistcoat.png');
width: 114px;
height: 90px;
}
.slim_armor_armoire_robeOfDiamonds { .slim_armor_armoire_robeOfDiamonds {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_robeOfDiamonds.png'); background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_robeOfDiamonds.png');
width: 114px; width: 114px;
@@ -35600,6 +35645,26 @@
width: 114px; width: 114px;
height: 90px; height: 90px;
} }
.back_mystery_202507 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/back_mystery_202507.png');
width: 117px;
height: 120px;
}
.head_mystery_202507 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_mystery_202507.png');
width: 114px;
height: 90px;
}
.shield_mystery_202508 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_mystery_202508.png');
width: 117px;
height: 120px;
}
.weapon_mystery_202508 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_mystery_202508.png');
width: 117px;
height: 120px;
}
.broad_armor_mystery_301404 { .broad_armor_mystery_301404 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_mystery_301404.png'); background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_mystery_301404.png');
width: 90px; width: 90px;

View File

@@ -46,13 +46,11 @@
.background { .background {
background-repeat: repeat-x; background-repeat: repeat-x;
height:216px;
width: 100%; width: 100%;
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
@@ -67,6 +65,13 @@
flex-direction: column; flex-direction: column;
} }
.shop-message {
position: relative;
height: 76px;
margin: 71px auto;
width: 240px;
}
.npc { .npc {
position: absolute; position: absolute;
left: 0; left: 0;

View File

@@ -97,9 +97,9 @@ import { mapState } from '@/libs/store';
import Sprite from '@/components/ui/sprite'; import Sprite from '@/components/ui/sprite';
export default { export default {
components: [ components: {
Sprite, Sprite,
], },
data () { data () {
return { return {
maxHealth, maxHealth,

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="row standard-page col-12 d-flex justify-content-center"> <div class="row standard-page col-12 d-flex justify-content-center">
<div class="admin-panel-content"> <div class="admin-panel-content">
<h1>Admin Panel</h1> <h1>{{ $t("adminPanel") }}</h1>
<form <form
class="form-inline" class="form-inline"
@submit.prevent="searchUsers(userIdentifier)" @submit.prevent="searchUsers(userIdentifier)"
@@ -72,7 +72,7 @@ export default {
}, },
mounted () { mounted () {
this.$store.dispatch('common:setTitle', { this.$store.dispatch('common:setTitle', {
section: 'Admin Panel', section: this.$t('adminPanel'),
}); });
}, },
methods: { methods: {

View File

@@ -55,7 +55,7 @@
<script> <script>
import VueRouter from 'vue-router'; import VueRouter from 'vue-router';
import { mapState } from '@/libs/store'; import { mapState } from '@/libs/store';
import LoadingSpinner from '../ui/loadingSpinner'; import LoadingSpinner from '../../ui/loadingSpinner';
const { isNavigationFailure, NavigationFailureType } = VueRouter; const { isNavigationFailure, NavigationFailureType } = VueRouter;

View File

@@ -38,12 +38,17 @@
> >
<div class="custom-control custom-checkbox"> <div class="custom-control custom-checkbox">
<input <input
:id="permission.key"
v-model="hero.permissions[permission.key]" v-model="hero.permissions[permission.key]"
:disabled="!hasPermission(user, permission.key)" :disabled="!hasPermission(user, permission.key)
|| (hero.permissions.fullAccess && permission.key !== 'fullAccess')"
class="custom-control-input" class="custom-control-input"
type="checkbox" type="checkbox"
> >
<label class="custom-control-label"> <label
class="custom-control-label"
:for="permission.key"
>
{{ permission.name }}<br> {{ permission.name }}<br>
<small class="text-secondary">{{ permission.description }}</small> <small class="text-secondary">{{ permission.description }}</small>
</label> </label>
@@ -124,7 +129,10 @@
value="Save" value="Save"
class="btn btn-primary mt-1" class="btn btn-primary mt-1"
> >
<b v-if="hasUnsavedChanges" class="text-warning float-right"> <b
v-if="hasUnsavedChanges"
class="text-warning float-right"
>
Unsaved changes Unsaved changes
</b> </b>
</div> </div>
@@ -147,7 +155,7 @@ import markdownDirective from '@/directives/markdown';
import saveHero from '../mixins/saveHero'; import saveHero from '../mixins/saveHero';
import { mapState } from '@/libs/store'; import { mapState } from '@/libs/store';
import { userStateMixin } from '../../../mixins/userState'; import { userStateMixin } from '../../../../mixins/userState';
const permissionList = [ const permissionList = [
{ {
@@ -175,6 +183,11 @@ const permissionList = [
name: 'Challenge Admin', name: 'Challenge Admin',
description: 'Can create official habitica challenges and admin all challenges', description: 'Can create official habitica challenges and admin all challenges',
}, },
{
key: 'accessControl',
name: 'Access Control',
description: 'Can manage IP-Address, Client and E-Mail blockers',
},
{ {
key: 'coupons', key: 'coupons',
name: 'Coupon Creator', name: 'Coupon Creator',

View File

@@ -46,7 +46,7 @@
: :
<span :class="{ ownedItem: !item.neverOwned }">{{ item.text }}</span> <span :class="{ ownedItem: !item.neverOwned }">{{ item.text }}</span>
</span> </span>
- {{ itemType }}.{{item.key}} - <i> {{ item.set }}</i> - {{ itemType }}.{{ item.key }} - <i> {{ item.set }}</i>
<div <div
v-if="item.modified" v-if="item.modified"

View File

@@ -88,7 +88,7 @@
<contributor-details <contributor-details
:hero="hero" :hero="hero"
:hasUnsavedChanges="hasUnsavedChanges( :has-unsaved-changes="hasUnsavedChanges(
[hero.contributor, unModifiedHero.contributor], [hero.contributor, unModifiedHero.contributor],
[hero.permissions, unModifiedHero.permissions], [hero.permissions, unModifiedHero.permissions],
[hero.secret, unModifiedHero.secret], [hero.secret, unModifiedHero.secret],
@@ -149,7 +149,7 @@ import Achievements from './achievements.vue';
import UserHistory from './userHistory.vue'; import UserHistory from './userHistory.vue';
import Stats from './stats.vue'; import Stats from './stats.vue';
import { userStateMixin } from '../../../mixins/userState'; import { userStateMixin } from '../../../../mixins/userState';
export default { export default {
components: { components: {

View File

@@ -55,7 +55,9 @@
<strong class="col-sm-9 col-form-label"> <strong class="col-sm-9 col-form-label">
<span v-if="userIsPartyLeader">User is the party leader</span> <span v-if="userIsPartyLeader">User is the party leader</span>
<span v-else>Party leader is <span v-else>Party leader is
<router-link :to="{'name': 'userProfile', 'params': {'userId': groupPartyData.leader}}"> <router-link
:to="{'name': 'userProfile', 'params': {'userId': groupPartyData.leader}}"
>
{{ groupPartyData.leader }} {{ groupPartyData.leader }}
</router-link> </router-link>
</span> </span>
@@ -63,7 +65,10 @@
</div> </div>
<div <div
class="btn btn-danger" class="btn btn-danger"
@click="removeFromParty()">Remove from Party</div> @click="removeFromParty()"
>
Remove from Party
</div>
</div> </div>
<strong v-else>User is not in a party.</strong> <strong v-else>User is not in a party.</strong>
<div class="subsection-start"> <div class="subsection-start">

View File

@@ -1,11 +1,13 @@
<template> <template>
<form @submit.prevent="saveHero({hero: { <form
@submit.prevent="saveHero({hero: {
_id: hero._id, _id: hero._id,
flags: hero.flags, flags: hero.flags,
balance: hero.balance, balance: hero.balance,
auth: hero.auth, auth: hero.auth,
secret: hero.secret, secret: hero.secret,
}, msg: 'Privileges or Gems or Moderation Notes'})"> }, msg: 'Privileges or Gems or Moderation Notes'})"
>
<div class="card mt-2"> <div class="card mt-2">
<div class="card-header"> <div class="card-header">
<h3 <h3
@@ -14,7 +16,10 @@
@click="expand = !expand" @click="expand = !expand"
> >
Privileges, Gem Balance Privileges, Gem Balance
<b v-if="hasUnsavedChanges && !expand" class="text-warning float-right"> <b
v-if="hasUnsavedChanges && !expand"
class="text-warning float-right"
>
Unsaved changes Unsaved changes
</b> </b>
</h3> </h3>
@@ -133,7 +138,10 @@
value="Save" value="Save"
class="btn btn-primary mt-1" class="btn btn-primary mt-1"
> >
<b v-if="hasUnsavedChanges" class="text-warning float-right"> <b
v-if="hasUnsavedChanges"
class="text-warning float-right"
>
Unsaved changes Unsaved changes
</b> </b>
</div> </div>

View File

@@ -8,7 +8,10 @@
@click="expand = !expand" @click="expand = !expand"
> >
Stats Stats
<b v-if="hasUnsavedChanges && !expand" class="text-warning float-right"> <b
v-if="hasUnsavedChanges && !expand"
class="text-warning float-right"
>
Unsaved changes Unsaved changes
</b> </b>
</h3> </h3>
@@ -18,33 +21,38 @@
class="card-body" class="card-body"
> >
<stats-row <stats-row
v-model="hero.stats.hp"
label="Health" label="Health"
color="red-label" color="red-label"
:max="maxHealth" :max="maxHealth"
v-model="hero.stats.hp" /> />
<stats-row <stats-row
v-model="hero.stats.exp"
label="Experience" label="Experience"
color="yellow-label" color="yellow-label"
min="0" min="0"
:max="maxFieldHardCap" :max="maxFieldHardCap"
v-model="hero.stats.exp" /> />
<stats-row <stats-row
v-model="hero.stats.mp"
label="Mana" label="Mana"
color="blue-label" color="blue-label"
min="0" min="0"
:max="maxFieldHardCap" :max="maxFieldHardCap"
v-model="hero.stats.mp" /> />
<stats-row <stats-row
v-model="hero.stats.lvl"
label="Level" label="Level"
step="1" step="1"
min="0" min="0"
:max="maxLevelHardCap" :max="maxLevelHardCap"
v-model="hero.stats.lvl" /> />
<stats-row <stats-row
v-model="hero.stats.gp"
label="Gold" label="Gold"
min="0" min="0"
:max="maxFieldHardCap" :max="maxFieldHardCap"
v-model="hero.stats.gp" /> />
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label">Selected Class</label> <label class="col-sm-3 col-form-label">Selected Class</label>
<div class="col-sm-9"> <div class="col-sm-9">
@@ -54,10 +62,18 @@
class="form-control" class="form-control"
:disabled="hero.stats.lvl < 10" :disabled="hero.stats.lvl < 10"
> >
<option value="warrior">Warrior</option> <option value="warrior">
<option value="wizard">Mage</option> Warrior
<option value="healer">Healer</option> </option>
<option value="rogue">Rogue</option> <option value="wizard">
Mage
</option>
<option value="healer">
Healer
</option>
<option value="rogue">
Rogue
</option>
</select> </select>
<small> <small>
When changing class, players usually need stat points deallocated as well. When changing class, players usually need stat points deallocated as well.
@@ -67,50 +83,59 @@
<h3>Stat Points</h3> <h3>Stat Points</h3>
<stats-row <stats-row
v-model="hero.stats.points"
label="Unallocated" label="Unallocated"
min="0" min="0"
step="1" step="1"
:max="maxStatPoints" :max="maxStatPoints"
v-model="hero.stats.points" /> />
<stats-row <stats-row
v-model="hero.stats.str"
label="Strength" label="Strength"
color="red-label" color="red-label"
min="0" min="0"
:max="maxStatPoints" :max="maxStatPoints"
step="1" step="1"
v-model="hero.stats.str" /> />
<stats-row <stats-row
v-model="hero.stats.int"
label="Intelligence" label="Intelligence"
color="blue-label" color="blue-label"
min="0" min="0"
:max="maxStatPoints" :max="maxStatPoints"
step="1" step="1"
v-model="hero.stats.int" /> />
<stats-row <stats-row
v-model="hero.stats.per"
label="Perception" label="Perception"
color="purple-label" color="purple-label"
min="0" min="0"
:max="maxStatPoints" :max="maxStatPoints"
step="1" step="1"
v-model="hero.stats.per" /> />
<stats-row <stats-row
v-model="hero.stats.con"
label="Constitution" label="Constitution"
color="yellow-label" color="yellow-label"
min="0" min="0"
:max="maxStatPoints" :max="maxStatPoints"
step="1" step="1"
v-model="hero.stats.con" /> />
<div class="form-group row"> <div class="form-group row">
<div class="offset-sm-3 col-sm-9"> <div class="offset-sm-3 col-sm-9">
<button <button
type="button" type="button"
class="btn btn-warning btn-sm" class="btn btn-warning btn-sm"
@click="deallocateStatPoints"> @click="deallocateStatPoints"
>
Deallocate all stat points Deallocate all stat points
</button> </button>
</div> </div>
</div> </div>
<div class="form-group row" v-if="statPointsIncorrect"> <div
v-if="statPointsIncorrect"
class="form-group row"
>
<div class="offset-sm-3 col-sm-9 text-danger"> <div class="offset-sm-3 col-sm-9 text-danger">
Error: Sum of stat points should equal the users level Error: Sum of stat points should equal the users level
</div> </div>
@@ -118,35 +143,40 @@
<h3>Buffs</h3> <h3>Buffs</h3>
<stats-row <stats-row
v-model="hero.stats.buffs.str"
label="Strength" label="Strength"
color="red-label" color="red-label"
min="0" min="0"
step="1" step="1"
v-model="hero.stats.buffs.str" /> />
<stats-row <stats-row
v-model="hero.stats.buffs.int"
label="Intelligence" label="Intelligence"
color="blue-label" color="blue-label"
min="0" min="0"
step="1" step="1"
v-model="hero.stats.buffs.int" /> />
<stats-row <stats-row
v-model="hero.stats.buffs.per"
label="Perception" label="Perception"
color="purple-label" color="purple-label"
min="0" min="0"
step="1" step="1"
v-model="hero.stats.buffs.per" /> />
<stats-row <stats-row
v-model="hero.stats.buffs.con"
label="Constitution" label="Constitution"
color="yellow-label" color="yellow-label"
min="0" min="0"
step="1" step="1"
v-model="hero.stats.buffs.con" /> />
<div class="form-group row"> <div class="form-group row">
<div class="offset-sm-3 col-sm-9"> <div class="offset-sm-3 col-sm-9">
<button <button
type="button" type="button"
class="btn btn-warning btn-sm" class="btn btn-warning btn-sm"
@click="resetBuffs"> @click="resetBuffs"
>
Reset Buffs Reset Buffs
</button> </button>
</div> </div>
@@ -161,7 +191,10 @@
value="Save" value="Save"
class="btn btn-primary mt-1" class="btn btn-primary mt-1"
> >
<b v-if="hasUnsavedChanges" class="text-warning float-right"> <b
v-if="hasUnsavedChanges"
class="text-warning float-right"
>
Unsaved changes Unsaved changes
</b> </b>
</div> </div>
@@ -189,7 +222,7 @@ import markdownDirective from '@/directives/markdown';
import saveHero from '../mixins/saveHero'; import saveHero from '../mixins/saveHero';
import { mapState } from '@/libs/store'; import { mapState } from '@/libs/store';
import { userStateMixin } from '../../../mixins/userState'; import { userStateMixin } from '../../../../mixins/userState';
import StatsRow from './stats-row'; import StatsRow from './stats-row';

View File

@@ -6,14 +6,19 @@
}, msg: 'Subscription Perks' })" }, msg: 'Subscription Perks' })"
> >
<div class="card mt-2"> <div class="card mt-2">
<div class="card-header" <div
@click="expand = !expand"> class="card-header"
@click="expand = !expand"
>
<h3 <h3
class="mb-0 mt-0" class="mb-0 mt-0"
:class="{ 'open': expand }" :class="{ 'open': expand }"
> >
Subscription, Monthly Perks Subscription, Monthly Perks
<b v-if="hasUnsavedChanges && !expand" class="text-warning float-right"> <b
v-if="hasUnsavedChanges && !expand"
class="text-warning float-right"
>
Unsaved changes Unsaved changes
</b> </b>
</h3> </h3>
@@ -29,10 +34,11 @@
Payment method: Payment method:
</label> </label>
<div class="col-sm-9"> <div class="col-sm-9">
<input v-model="hero.purchased.plan.paymentMethod" <input
v-if="!isRegularPaymentMethod"
v-model="hero.purchased.plan.paymentMethod"
class="form-control" class="form-control"
type="text" type="text"
v-if="!isRegularPaymentMethod"
> >
<select <select
v-else v-else
@@ -40,14 +46,30 @@
class="form-control" class="form-control"
type="text" type="text"
> >
<option value="groupPlan">Group Plan</option> <option value="groupPlan">
<option value="Stripe">Stripe</option> Group Plan
<option value="Apple">Apple</option> </option>
<option value="Google">Google</option> <option value="Stripe">
<option value="Amazon Payments">Amazon</option> Stripe
<option value="PayPal">PayPal</option> </option>
<option value="Gift">Gift</option> <option value="Apple">
<option value="">Clear out</option> Apple
</option>
<option value="Google">
Google
</option>
<option value="Amazon Payments">
Amazon
</option>
<option value="PayPal">
PayPal
</option>
<option value="Gift">
Gift
</option>
<option value="">
Clear out
</option>
</select> </select>
</div> </div>
</div> </div>
@@ -58,10 +80,11 @@
Payment schedule: Payment schedule:
</label> </label>
<div class="col-sm-9"> <div class="col-sm-9">
<input v-model="hero.purchased.plan.planId" <input
v-if="!isRegularPlanId"
v-model="hero.purchased.plan.planId"
class="form-control" class="form-control"
type="text" type="text"
v-if="!isRegularPlanId"
> >
<select <select
v-else v-else
@@ -69,13 +92,27 @@
class="form-control" class="form-control"
type="text" type="text"
> >
<option value="basic_earned">Monthly recurring</option> <option value="basic_earned">
<option value="basic_3mo">3 Months recurring</option> Monthly recurring
<option value="basic_6mo">6 Months recurring</option> </option>
<option value="basic_12mo">12 Months recurring</option> <option value="basic_3mo">
<option value="group_monthly">Group Plan (legacy)</option> 3 Months recurring
<option value="group_plan_auto">Group Plan (auto)</option> </option>
<option value="">Clear out</option> <option value="basic_6mo">
6 Months recurring
</option>
<option value="basic_12mo">
12 Months recurring
</option>
<option value="group_monthly">
Group Plan (legacy)
</option>
<option value="group_plan_auto">
Group Plan (auto)
</option>
<option value="">
Clear out
</option>
</select> </select>
</div> </div>
</div> </div>
@@ -93,27 +130,31 @@
> >
</div> </div>
</div> </div>
<div class="form-group row" <div
v-if="hero.purchased.plan.planId === 'group_plan_auto'"> v-if="hero.purchased.plan.planId === 'group_plan_auto'"
class="form-group row"
>
<label class="col-sm-3 col-form-label"> <label class="col-sm-3 col-form-label">
Group Plan Memberships: Group Plan Memberships:
</label> </label>
<div class="col-sm-9 col-form-label"> <div class="col-sm-9 col-form-label">
<loading-spinner <loading-spinner
v-if="!groupPlans" v-if="!groupPlans"
dark-color=true dark-color="true"
/> />
<b <b
v-else-if="groupPlans.length === 0" v-else-if="groupPlans.length === 0"
class="text-danger col-form-label" class="text-danger col-form-label"
>User is not part of an active group plan!</b> >User is not part of an active group plan!</b>
<div <div
v-else
v-for="group in groupPlans" v-for="group in groupPlans"
v-else
:key="group._id" :key="group._id"
class="card mb-2"> class="card mb-2"
>
<div class="card-body"> <div class="card-body">
<h6 class="card-title">{{ group.name }} <h6 class="card-title">
{{ group.name }}
<small class="float-right">{{ group._id }}</small> <small class="float-right">{{ group._id }}</small>
</h6> </h6>
<p class="card-text"> <p class="card-text">
@@ -122,7 +163,10 @@
v-if="group.leader !== hero._id" v-if="group.leader !== hero._id"
@click="switchUser(group.leader)" @click="switchUser(group.leader)"
>{{ group.leader }}</a> >{{ group.leader }}</a>
<strong v-else class="text-success">This user</strong> <strong
v-else
class="text-success"
>This user</strong>
</p> </p>
<p class="card-text"> <p class="card-text">
<strong>Members: </strong> {{ group.memberCount }} <strong>Members: </strong> {{ group.memberCount }}
@@ -190,16 +234,21 @@
<strong class="input-group-text"> <strong class="input-group-text">
{{ dateFormat(hero.purchased.plan.dateTerminated) }} {{ dateFormat(hero.purchased.plan.dateTerminated) }}
</strong> </strong>
<a class="btn btn-danger" <a
href="#" v-if="!hero.purchased.plan.dateTerminated && hero.purchased.plan.planId"
v-b-modal.sub_termination_modal v-b-modal.sub_termination_modal
v-if="!hero.purchased.plan.dateTerminated && hero.purchased.plan.planId"> class="btn btn-danger"
href="#"
>
Terminate Terminate
</a> </a>
</div> </div>
</div> </div>
<small v-if="!hero.purchased.plan.dateTerminated <small
&& hero.purchased.plan.planId" class="text-success"> v-if="!hero.purchased.plan.dateTerminated
&& hero.purchased.plan.planId"
class="text-success"
>
The subscription does not have a termination date and is active. The subscription does not have a termination date and is active.
</small> </small>
</div> </div>
@@ -235,9 +284,11 @@
step="any" step="any"
> >
<div class="input-group-append"> <div class="input-group-append">
<a class="btn btn-warning" <a
v-if="hero.purchased.plan.dateTerminated && hero.purchased.plan.extraMonths > 0"
class="btn btn-warning"
@click="applyExtraMonths" @click="applyExtraMonths"
v-if="hero.purchased.plan.dateTerminated && hero.purchased.plan.extraMonths > 0"> >
Apply Credit Apply Credit
</a> </a>
</div> </div>
@@ -339,19 +390,24 @@
</span> </span>
</div> </div>
</div> </div>
<div class="form-group row" <div
v-if="!isConvertingToGroupPlan && hero.purchased.plan.planId !== 'group_plan_auto'"> v-if="!isConvertingToGroupPlan && hero.purchased.plan.planId !== 'group_plan_auto'"
class="form-group row"
>
<div class="offset-sm-3 col-sm-9"> <div class="offset-sm-3 col-sm-9">
<button <button
type="button" type="button"
class="btn btn-secondary btn-sm" class="btn btn-secondary btn-sm"
@click="beginGroupPlanConvert"> @click="beginGroupPlanConvert"
>
Begin converting to group plan subscription Begin converting to group plan subscription
</button> </button>
</div> </div>
</div> </div>
<div class="form-group row" <div
v-if="isConvertingToGroupPlan"> v-if="isConvertingToGroupPlan"
class="form-group row"
>
<label class="col-sm-3 col-form-label"> <label class="col-sm-3 col-form-label">
Group Plan group ID: Group Plan group ID:
</label> </label>
@@ -374,25 +430,40 @@
class="btn btn-primary mt-1" class="btn btn-primary mt-1"
@click="saveClicked" @click="saveClicked"
> >
<b v-if="hasUnsavedChanges" class="text-warning float-right"> <b
v-if="hasUnsavedChanges"
class="text-warning float-right"
>
Unsaved changes Unsaved changes
</b> </b>
</div> </div>
</div> </div>
<b-modal id="sub_termination_modal" title="Set Termination Date"> <b-modal
id="sub_termination_modal"
title="Set Termination Date"
>
<p> <p>
You can set the sub benefit termination date to today or to the last You can set the sub benefit termination date to today or to the last
day of the current billing cycle. Any extra subscription credit will day of the current billing cycle. Any extra subscription credit will
then be processed and automatically added onto the selected date. then be processed and automatically added onto the selected date.
</p> </p>
<template #modal-footer> <template #modal-footer>
<div class="mt-3 btn btn-secondary" @click="$bvModal.hide('sub_termination_modal')"> <div
class="mt-3 btn btn-secondary"
@click="$bvModal.hide('sub_termination_modal')"
>
Close Close
</div> </div>
<div class="mt-3 btn btn-danger" @click="terminateSubscription()"> <div
class="mt-3 btn btn-danger"
@click="terminateSubscription()"
>
Set to Today Set to Today
</div> </div>
<div class="mt-3 btn btn-danger" @click="terminateSubscription(todayWithRemainingCycle)"> <div
class="mt-3 btn btn-danger"
@click="terminateSubscription(todayWithRemainingCycle)"
>
Set to {{ todayWithRemainingCycle.utc().format('MM/DD/YYYY') }} Set to {{ todayWithRemainingCycle.utc().format('MM/DD/YYYY') }}
</div> </div>
</template> </template>
@@ -420,15 +491,15 @@
import isUUID from 'validator/es/lib/isUUID'; import isUUID from 'validator/es/lib/isUUID';
import moment from 'moment'; import moment from 'moment';
import { getPlanContext } from '@/../../common/script/cron'; import { getPlanContext } from '@/../../common/script/cron';
import subscriptionBlocks from '@/../../common/script/content/subscriptionBlocks';
import saveHero from '../mixins/saveHero'; import saveHero from '../mixins/saveHero';
import subscriptionBlocks from '../../../../../common/script/content/subscriptionBlocks';
import LoadingSpinner from '@/components/ui/loadingSpinner'; import LoadingSpinner from '@/components/ui/loadingSpinner';
export default { export default {
mixins: [saveHero],
components: { components: {
LoadingSpinner, LoadingSpinner,
}, },
mixins: [saveHero],
props: { props: {
hero: { hero: {
type: Object, type: Object,

View File

@@ -22,8 +22,8 @@
</template> </template>
<script> <script>
import PurchaseHistoryTable from '../../ui/purchaseHistoryTable.vue'; import PurchaseHistoryTable from '../../../ui/purchaseHistoryTable.vue';
import { userStateMixin } from '../../../mixins/userState'; import { userStateMixin } from '../../../../mixins/userState';
export default { export default {
components: { components: {

View File

@@ -180,7 +180,7 @@
<script> <script>
import moment from 'moment'; import moment from 'moment';
import { userStateMixin } from '../../../mixins/userState'; import { userStateMixin } from '../../../../mixins/userState';
export default { export default {
filters: { filters: {

View File

@@ -13,7 +13,10 @@
@click="expand = !expand" @click="expand = !expand"
> >
User Profile User Profile
<b v-if="hasUnsavedChanges && !expand" class="text-warning float-right"> <b
v-if="hasUnsavedChanges && !expand"
class="text-warning float-right"
>
Unsaved changes Unsaved changes
</b> </b>
</h3> </h3>
@@ -66,7 +69,10 @@
value="Save" value="Save"
class="btn btn-primary mt-1" class="btn btn-primary mt-1"
> >
<b v-if="hasUnsavedChanges" class="text-warning float-right"> <b
v-if="hasUnsavedChanges"
class="text-warning float-right"
>
Unsaved changes Unsaved changes
</b> </b>
</div> </div>
@@ -86,7 +92,7 @@ import markdownDirective from '@/directives/markdown';
import saveHero from '../mixins/saveHero'; import saveHero from '../mixins/saveHero';
import { mapState } from '@/libs/store'; import { mapState } from '@/libs/store';
import { userStateMixin } from '../../../mixins/userState'; import { userStateMixin } from '../../../../mixins/userState';
function resetData (self) { function resetData (self) {
self.expand = false; self.expand = false;

View File

@@ -0,0 +1,133 @@
<template>
<div style="display: contents">
<td>
<select
v-model="blocker.type"
class="form-control"
@change="onTypeChanged"
>
<option value="ipaddress">
IP-Address
</option>
<option value="client">
Client Identifier
</option>
<option value="email">
E-Mail
</option>
</select>
</td>
<td>
<select
v-model="blocker.area"
class="form-control"
>
<option value="full">
Full
</option>
</select>
</td>
<td>
<input
v-model="blocker.value"
class="form-control"
autocorrect="off"
autocapitalize="off"
:class="{ 'is-invalid input-invalid': !isValid }"
@input="validateValue"
>
</td>
<td>
<input
v-model="blocker.reason"
class="form-control"
>
</td>
<td
colspan="3"
class="text-right"
>
<button
class="btn btn-primary mr-2"
:disabled="!isValid"
:class="{ disabled: !isValid }"
@click="$emit('save', blocker)"
>
<span>Save</span>
</button>
<button
class="btn btn-danger"
@click="$emit('cancel')"
>
<span>Cancel</span>
</button>
</td>
</div>
</template>
<style lang="scss" scoped>
.btn-primary.disabled {
background: #4F2A93;
color: white;
cursor: not-allowed;
opacity: 0.5;
}
</style>
<script>
import isIP from 'validator/es/lib/isIP';
export default {
name: 'BlockerForm',
props: {
isNew: {
type: Boolean,
default: false,
},
blocker: {
type: Object,
default: () => ({
type: '',
area: '',
value: '',
reason: '',
}),
},
},
data () {
return {
isValid: false,
};
},
mounted () {
this.validateValue();
},
methods: {
onTypeChanged () {
if (this.blocker.type === 'email') {
this.blocker.area = 'full';
}
this.validateValue();
},
validateValue () {
if (this.blocker.type === 'ipaddress') {
this.validateValueAsIpAddress();
} else if (this.blocker.type === 'client') {
this.validateValueAsClient();
} else if (this.blocker.type === 'email') {
this.validateValueAsEmail();
}
},
validateValueAsEmail () {
const emailRegex = /^([a-zA-Z0-9._%+-]*)@(?:[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})?$/;
this.isValid = emailRegex.test(this.blocker.value) && this.blocker.value.length > 3;
},
validateValueAsIpAddress () {
this.isValid = isIP(this.blocker.value);
},
validateValueAsClient () {
this.isValid = this.blocker.value.length > 0;
},
},
};
</script>

View File

@@ -0,0 +1,238 @@
<template>
<div class="row standard-page col-12 d-flex justify-content-center">
<div class="blocker-content">
<h1>
Blockers
<button
class="btn btn-primary float-right"
@click="showCreateForm = true"
>
Create
</button>
</h1>
<table class="table table-bordered">
<thead>
<tr>
<th>
Type <span
id="type_tooltip"
class="info-icon"
>?</span>
<b-tooltip
target="type_tooltip"
>
<b>IP-Address</b> - Block access for a specific IP-Address
<br>
<br>
<b>Client</b> - Block access for a client based on the "x-client" header.
<br>
<br>
<b>E-Mail</b> - Blocks e-mails from being used for signup.
</b-tooltip>
</th>
<th>
Area <span
id="area_tooltip"
class="info-icon"
>?</span>
<b-tooltip
target="area_tooltip"
>
<b>Full</b> - Block access to the entire site.
<br>
<br>
<b>Payments</b> - Block access to any payment related functionality.
</b-tooltip>
</th>
<th>Value</th>
<th>Reason</th>
<th>Source</th>
<th>Created at</th>
<th class="btncol"></th>
</tr>
</thead>
<tbody>
<tr v-if="showCreateForm">
<BlockerForm
:is-new="true"
:blocker="newBlocker"
@save="createBlocker"
@cancel="showCreateForm = false"
/>
</tr>
<tr
v-for="blocker in blockers"
:key="blocker._id"
>
<BlockerForm
v-if="blocker._id === editedBlockerId"
:blocker="blocker"
@save="saveBlocker(blocker)"
@cancel="editedBlockerId = null"
/>
<template v-else>
<td>{{ getTypeName(blocker.type) }}</td>
<td>{{ getAreaName(blocker.area) }}</td>
<td>{{ blocker.value }}</td>
<td>{{ blocker.reason || "--" }}</td>
<td>{{ blocker.blockSource }}</td>
<td>{{ blocker.createdAt }}</td>
<td>
<button
class="btn btn-primary mr-2"
@click="editBlocker(blocker._id)"
>
<span
v-once
class="svg-icon icon-16"
v-html="icons.editIcon"
></span>
</button>
<button
class="btn btn-danger"
@click="deleteBlocker(blocker._id)"
>
<span
v-once
class="svg-icon icon-16"
v-html="icons.deleteIcon"
></span>
</button>
</td>
</template>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<style lang="scss" scoped>
@import '@/assets/scss/colors.scss';
.blocker-content {
flex: 0 0 100%;
max-width: 1200px;
}
.btn {
padding: 0.4rem 0.75rem;
}
.btncol {
width: 123px;
}
td {
font-size: 1rem;
}
.info-icon {
font-size: 0.8rem;
color: $purple-400;
cursor: pointer;
margin-left: 0.5rem;
background-color: $gray-500;
padding: 0.1rem 0.3rem;
border-radius: 0.2rem;
}
.info-icon:hover {
background-color: $purple-400;
color: white;
}
</style>
<script>
import { mapState } from '@/libs/store';
import editIcon from '@/assets/svg/edit.svg?raw';
import deleteIcon from '@/assets/svg/delete.svg?raw';
import BlockerForm from './blocker_form.vue';
export default {
components: {
BlockerForm,
},
data () {
return {
showCreateForm: false,
newBlocker: {
type: '',
area: 'full',
value: '',
reason: '',
},
blockers: [],
editedBlockerId: null,
icons: Object.freeze({
editIcon,
deleteIcon,
}),
};
},
computed: {
...mapState({ user: 'user.data' }),
},
mounted () {
this.$store.dispatch('common:setTitle', {
section: this.$t('siteBlockers'),
});
this.loadBlockers();
},
methods: {
async loadBlockers () {
this.blockers = await this.$store.dispatch('blockers:getBlockers');
},
editBlocker (id) {
this.editedBlockerId = id;
},
async saveBlocker (blocker) {
await this.$store.dispatch('blockers:updateBlocker', { blocker });
this.editedBlockerId = null;
this.loadBlockers();
},
async deleteBlocker (blockerId) {
if (!window.confirm('Are you sure you want to delete this blocker?')) {
return;
}
await this.$store.dispatch('blockers:deleteBlocker', { blockerId });
this.loadBlockers();
},
async createBlocker (blocker) {
await this.$store.dispatch('blockers:createBlocker', { blocker });
this.showCreateForm = false;
this.newBlocker = {
type: '',
area: 'full',
value: '',
reason: '',
};
this.loadBlockers();
},
getTypeName (type) {
switch (type) {
case 'ipaddress':
return 'IP-Address';
case 'email':
return 'E-Mail';
case 'client':
return 'Client Identifier';
default:
return type;
}
},
getAreaName (area) {
switch (area) {
case 'full':
return 'Full';
case 'payments':
return 'Payments';
default:
return area;
}
},
},
};
</script>

View File

@@ -0,0 +1,40 @@
<template>
<div class="row">
<secondary-menu class="col-12">
<router-link
v-if="hasPermission(user, 'userSupport')"
class="nav-link"
:to="{name: 'adminPanel'}"
>
{{ $t('adminPanel') }}
</router-link>
<router-link
v-if="hasPermission(user, 'accessControl')"
class="nav-link"
:to="{name: 'blockers'}"
>
{{ $t('siteBlockers') }}
</router-link>
</secondary-menu><div class="col-12">
<router-view />
</div>
</div>
</template>
<script>
import { mapState } from '@/libs/store';
import SecondaryMenu from '@/components/secondaryMenu';
import { userStateMixin } from '../../mixins/userState';
export default {
components: {
SecondaryMenu,
},
mixins: [
userStateMixin,
],
computed: {
...mapState({ user: 'user.data' }),
},
};
</script>

View File

@@ -276,9 +276,9 @@
</div> </div>
<div <div
class="time-travel"
v-if="TIME_TRAVEL_ENABLED && user?.permissions?.fullAccess" v-if="TIME_TRAVEL_ENABLED && user?.permissions?.fullAccess"
:key="lastTimeJump" :key="lastTimeJump"
class="time-travel"
> >
<a <a
class="btn btn-secondary mr-1" class="btn btn-secondary mr-1"

View File

@@ -220,7 +220,6 @@
v-if="forgotPassword" v-if="forgotPassword"
id="forgot-form" id="forgot-form"
@submit.prevent="handleSubmit" @submit.prevent="handleSubmit"
@keyup.enter="handleSubmit"
> >
<div class="text-center"> <div class="text-center">
<div> <div>
@@ -268,7 +267,6 @@
v-if="resetPasswordSetNewOne" v-if="resetPasswordSetNewOne"
id="reset-password-set-new-one-form" id="reset-password-set-new-one-form"
@submit.prevent="handleSubmit" @submit.prevent="handleSubmit"
@keyup.enter="handleSubmit"
> >
<div class="text-center"> <div class="text-center">
<div> <div>

View File

@@ -30,6 +30,7 @@
<script> <script>
import markdownDirective from '@/directives/markdown'; import markdownDirective from '@/directives/markdown';
import { LOCALSTORAGE_AUTH_KEY } from '@/libs/auth';
const COMMUNITY_MANAGER_EMAIL = import.meta.env.EMAILS_COMMUNITY_MANAGER_EMAIL; // eslint-disable-line const COMMUNITY_MANAGER_EMAIL = import.meta.env.EMAILS_COMMUNITY_MANAGER_EMAIL; // eslint-disable-line
@@ -39,12 +40,14 @@ export default {
}, },
computed: { computed: {
bannedMessage () { bannedMessage () {
const AUTH_SETTINGS = localStorage.getItem('habit-mobile-settings'); const AUTH_SETTINGS = localStorage.getItem(LOCALSTORAGE_AUTH_KEY);
const parseSettings = JSON.parse(AUTH_SETTINGS); const parseSettings = JSON.parse(AUTH_SETTINGS);
const userId = parseSettings ? parseSettings.auth.apiId : ''; const userId = parseSettings ? parseSettings.auth.apiId : '';
const username = this.$store?.state?.user?.data?.auth?.local?.username || '';
return this.$t('accountSuspended', { return this.$t('accountSuspended', {
userId, userId,
username,
communityManagerEmail: COMMUNITY_MANAGER_EMAIL, communityManagerEmail: COMMUNITY_MANAGER_EMAIL,
}); });
}, },

View File

@@ -227,7 +227,8 @@
<div class="quest-icon"> <div class="quest-icon">
<Sprite <Sprite
class="quest" class="quest"
:image-name="`inventory_quest_scroll_${questData.key}`" /> :image-name="`inventory_quest_scroll_${questData.key}`"
/>
</div> </div>
</div> </div>
<div <div

View File

@@ -286,7 +286,7 @@
:to="{ name: 'adminPanelUser', :to="{ name: 'adminPanelUser',
params: { userIdentifier: hero._id } }" params: { userIdentifier: hero._id } }"
> >
admin panel {{ $t("adminPanel") }}
</router-link> </router-link>
</span> </span>
</td> </td>

View File

@@ -295,14 +295,6 @@
{{ $t('help') }} {{ $t('help') }}
</router-link> </router-link>
<div class="topbar-dropdown"> <div class="topbar-dropdown">
<router-link
v-if="user.permissions.fullAccess ||
user.permissions.userSupport"
class="topbar-dropdown-item dropdown-item"
:to="{name: 'adminPanel'}"
>
Admin Panel
</router-link>
<router-link <router-link
class="topbar-dropdown-item dropdown-item" class="topbar-dropdown-item dropdown-item"
:to="{name: 'faq'}" :to="{name: 'faq'}"
@@ -336,6 +328,61 @@
>{{ $t('requestFeature') }}</a> >{{ $t('requestFeature') }}</a>
</div> </div>
</li> </li>
<li
v-if="hasElevatedPrivileges"
class="topbar-item droppable"
:class="{
'active': $route.path.startsWith('/admin')}"
>
<div
class="chevron rotate"
@click="dropdownMobile($event)"
>
<div
v-once
class="chevron-icon-down"
v-html="icons.chevronDown"
></div>
</div>
<router-link
v-if="hasPermission(user, 'userSupport')"
class="nav-link"
:to="{name: 'adminPanel'}"
>
{{ $t('admin') }}
</router-link>
<a
v-else
href="#"
class="nav-link"
>
{{ $t('admin') }}
</a>
<div class="topbar-dropdown">
<router-link
v-if="hasPermission(user, 'userSupport')"
class="topbar-dropdown-item dropdown-item"
:to="{name: 'adminPanel'}"
>
{{ $t("adminPanel") }}
</router-link>
<router-link
v-if="hasPermission(user, 'accessControl')"
class="topbar-dropdown-item dropdown-item"
:to="{name: 'blockers'}"
>
{{ $t("siteBlockers") }}
</router-link>
<a
v-if="hasPermission(user, 'news')"
class="topbar-dropdown-item dropdown-item"
target="_blank"
href="https://panel.habitica.com"
>
{{ $t('newsroom') }}
</a>
</div>
</li>
</b-navbar-nav> </b-navbar-nav>
<div class="currency-tray form-inline"> <div class="currency-tray form-inline">
<div <div
@@ -757,6 +804,7 @@ import selectUserModal from '@/components/payments/selectUserModal';
import sync from '@/mixins/sync'; import sync from '@/mixins/sync';
import userDropdown from './userDropdown'; import userDropdown from './userDropdown';
import reportBug from '@/mixins/reportBug.js'; import reportBug from '@/mixins/reportBug.js';
import { userStateMixin } from '../../mixins/userState';
export default { export default {
components: { components: {
@@ -769,7 +817,7 @@ export default {
selectUserModal, selectUserModal,
userDropdown, userDropdown,
}, },
mixins: [sync, reportBug], mixins: [sync, reportBug, userStateMixin],
data () { data () {
return { return {
isUserDropdownOpen: false, isUserDropdownOpen: false,
@@ -802,6 +850,12 @@ export default {
params: { groupId: this.groupPlans[0]._id }, params: { groupId: this.groupPlans[0]._id },
}; };
}, },
hasElevatedPrivileges () {
return this.user.permissions.fullAccess
|| this.user.permissions.userSupport
|| this.user.permissions.accessControl
|| this.user.permissions.news;
},
}, },
async mounted () { async mounted () {
await this.getUserGroupPlans(); await this.getUserGroupPlans();

View File

@@ -15,7 +15,8 @@
<Sprite <Sprite
slot="icon" slot="icon"
class="mt-3" class="mt-3"
:image-name="notification.data.icon" /> :image-name="notification.data.icon"
/>
</base-notification> </base-notification>
</template> </template>

View File

@@ -12,7 +12,8 @@
></div> ></div>
<Sprite <Sprite
slot="icon" slot="icon"
:image-name="mysteryClass" /> :image-name="mysteryClass"
/>
</base-notification> </base-notification>
</template> </template>

View File

@@ -81,9 +81,10 @@ import moment from 'moment';
import habiticaMarkdown from 'habitica-markdown'; import habiticaMarkdown from 'habitica-markdown';
import { mapState } from '@/libs/store'; import { mapState } from '@/libs/store';
import seasonalNPC from '@/mixins/seasonalNPC'; import seasonalNPC from '@/mixins/seasonalNPC';
import { userStateMixin } from '../../mixins/userState';
export default { export default {
mixins: [seasonalNPC], mixins: [seasonalNPC, userStateMixin],
data () { data () {
return { return {
posts: [], posts: [],
@@ -107,7 +108,7 @@ export default {
if (lastPublishedPost) this.posts.push(lastPublishedPost); if (lastPublishedPost) this.posts.push(lastPublishedPost);
// If the user is authorized, show any draft // If the user is authorized, show any draft
if (this.user && (this.user.permissions.news || this.user.permissions.fullAccess)) { if (this.user && (this.hasPermission(this.user, 'news'))) {
this.posts.unshift( this.posts.unshift(
...postsFromServer ...postsFromServer
.filter(p => !p.published || moment().isBefore(p.publishDate)), .filter(p => !p.published || moment().isBefore(p.publishDate)),

View File

@@ -41,30 +41,58 @@
<div class="standard-page"> <div class="standard-page">
<div class="featuredItems"> <div class="featuredItems">
<div <div
v-if="isSubscribed || (hasTrinket && !isSubscribed)"
class="background" class="background"
:class="{'background-closed': closed, 'background-open': !closed }"
:style="{'background-image': imageURLs.background}" :style="{'background-image': imageURLs.background}"
> >
<div <div
class="npc" class="npc"
:class="{'closed': closed }"
:style="{'background-image': imageURLs.npc}" :style="{'background-image': imageURLs.npc}"
> >
<div class="featured-label"> <div class="featured-label">
<span class="rectangle"></span><span <span class="rectangle"></span>
<span
v-once v-once
class="text" class="text"
>{{ $t('timeTravelers') }}</span><span class="rectangle"></span>
</div>
</div><div
v-if="closed"
class="content"
> >
<div class="featured-label with-border closed"> {{ $t('timeTravelers') }}
<span class="rectangle"></span><span </span>
<span class="rectangle"></span>
</div>
</div>
</div>
<div class="content">
<div
class="background"
:style="{'background-image': imageURLs.background}"
>
<div
class="npc"
:style="{'background-image': imageURLs.npc}"
>
<div class="featured-label">
<span class="rectangle"></span>
<span
v-once v-once
class="text" class="text"
>{{ $t('timeTravelersPopoverNoSubMobile') }}</span><span class="rectangle"></span> >
{{ $t('timeTravelers') }}
</span>
<span class="rectangle"></span>
</div>
</div>
<div
v-if="!isSubscribed && !hasTrinket"
class="shop-message featured-label with-border closed"
>
<span class="rectangle"></span>
<span
v-once
class="text"
>
{{ $t('timeTravelersPopoverNoSubMobile') }}
</span>
<span class="rectangle"></span>
</div> </div>
</div> </div>
</div> </div>
@@ -126,7 +154,8 @@
</template> </template>
</itemRows> </itemRows>
</div> </div>
</div><buyQuestModal </div>
<buyQuestModal
:item="selectedItemToBuy || {}" :item="selectedItemToBuy || {}"
:price-type="selectedItemToBuy ? selectedItemToBuy.currency : ''" :price-type="selectedItemToBuy ? selectedItemToBuy.currency : ''"
:with-pin="true" :with-pin="true"
@@ -163,6 +192,7 @@ import _throttle from 'lodash/throttle';
import _groupBy from 'lodash/groupBy'; import _groupBy from 'lodash/groupBy';
import _map from 'lodash/map'; import _map from 'lodash/map';
import _find from 'lodash/find'; import _find from 'lodash/find';
import moment from 'moment';
import isPinned from '@/../../common/script/libs/isPinned'; import isPinned from '@/../../common/script/libs/isPinned';
import shops from '@/../../common/script/libs/shops'; import shops from '@/../../common/script/libs/shops';
import { mapState } from '@/libs/store'; import { mapState } from '@/libs/store';
@@ -233,15 +263,18 @@ export default {
userItems: 'user.data.items', userItems: 'user.data.items',
currentEventList: 'worldState.data.currentEventList', currentEventList: 'worldState.data.currentEventList',
}), }),
isSubscribed () {
closed () { const now = new Date();
return this.user.purchased.plan.consecutive.trinkets === 0; const { plan } = this.user.purchased;
return plan && plan.customerId
&& (!plan.dateTerminated || moment(plan.dateTerminated).isAfter(now));
},
hasTrinket () {
return this.user.purchased.plan.consecutive.trinkets > 0;
}, },
shop () { shop () {
return shops.getTimeTravelersShop(this.user); return shops.getTimeTravelersShop(this.user);
}, },
categories () { categories () {
const apiCategories = this.shop.categories; const apiCategories = this.shop.categories;
@@ -302,10 +335,8 @@ export default {
} }
}); });
this.currentEvent = _find(this.currentEventList, event => Boolean(['winter', 'spring', 'summer', 'fall'].includes(event.season))); this.currentEvent = _find(this.currentEventList, event => Boolean(['winter', 'spring', 'summer', 'fall'].includes(event.season)));
if (!this.currentEvent || !this.currentEvent.season || this.currentEvent.season === 'thanksgiving' || this.closed) { if (!this.currentEvent || !this.currentEvent.season || this.currentEvent.season === 'thanksgiving') {
this.imageURLs.background = 'url(/static/npc/normal/time_travelers_background.png)'; this.imageURLs.background = 'url(/static/npc/normal/time_travelers_background.png)';
this.imageURLs.npc = this.closed ? 'url(/static/npc/normal/time_travelers_closed_banner.png)'
: 'url(/static/npc/normal/time_travelers_open_banner.png)';
} else { } else {
this.imageURLs.background = `url(/static/npc/${this.currentEvent.season}/time_travelers_background.png)`; this.imageURLs.background = `url(/static/npc/${this.currentEvent.season}/time_travelers_background.png)`;
this.imageURLs.npc = `url(/static/npc/${this.currentEvent.season}/time_travelers_open_banner.png)`; this.imageURLs.npc = `url(/static/npc/${this.currentEvent.season}/time_travelers_open_banner.png)`;

View File

@@ -7,10 +7,15 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-12 mb-5 mb-md-0"> <div class="col-12 mb-5 mb-md-0">
<img :src="makeUrl('features_taskboard.png')" class="img-fluid"> <img
:src="makeUrl('features_taskboard.png')"
class="img-fluid"
>
<h2>{{ $t('marketing1Lead1Title') }}</h2> <h2>{{ $t('marketing1Lead1Title') }}</h2>
<div class="row justify-content-md-center"> <div class="row justify-content-md-center">
<p class="col col-lg-8 col-xl-6 margin-auto description">{{ $t('marketing1Lead1') }}</p> <p class="col col-lg-8 col-xl-6 margin-auto description">
{{ $t('marketing1Lead1') }}
</p>
</div> </div>
</div> </div>
</div> </div>
@@ -18,12 +23,16 @@
<div class="col-md-6 mb-5 mb-md-0"> <div class="col-md-6 mb-5 mb-md-0">
<img :src="makeUrl('features_gear.png')"> <img :src="makeUrl('features_gear.png')">
<h2>{{ $t('marketing1Lead2Title') }}</h2> <h2>{{ $t('marketing1Lead2Title') }}</h2>
<p class="description">{{ $t('marketing1Lead2') }}</p> <p class="description">
{{ $t('marketing1Lead2') }}
</p>
</div> </div>
<div class="col-md-6 mb-5 mb-md-0"> <div class="col-md-6 mb-5 mb-md-0">
<img :src="makeUrl('features_items.png')"> <img :src="makeUrl('features_items.png')">
<h2>{{ $t('marketing1Lead3Title') }}</h2> <h2>{{ $t('marketing1Lead3Title') }}</h2>
<p class="description">{{ $t('marketing1Lead3') }}</p> <p class="description">
{{ $t('marketing1Lead3') }}
</p>
</div> </div>
</div> </div>
<hr> <hr>
@@ -35,19 +44,26 @@
<div class="row mb-5"> <div class="row mb-5">
<div class="col-12"> <div class="col-12">
<h2>{{ $t('marketing2Lead1Title') }}</h2> <h2>{{ $t('marketing2Lead1Title') }}</h2>
<p class="description">{{ $t('marketing2Lead1') }}</p> <p class="description">
{{ $t('marketing2Lead1') }}
</p>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6 mb-5 mb-md-0"> <div class="col-md-6 mb-5 mb-md-0">
<img :src="makeUrl('features_monsters.png')"> <img :src="makeUrl('features_monsters.png')">
<h2>{{ $t('marketing2Lead2Title') }}</h2> <h2>{{ $t('marketing2Lead2Title') }}</h2>
<p class="description" v-markdown="$t('marketing2Lead2')"></p> <p
v-markdown="$t('marketing2Lead2')"
class="description"
></p>
</div> </div>
<div class="col-md-6 mb-5 mb-md-0"> <div class="col-md-6 mb-5 mb-md-0">
<img :src="makeUrl('features_challenges.png')"> <img :src="makeUrl('features_challenges.png')">
<h2>{{ $t('marketing2Lead3Title') }}</h2> <h2>{{ $t('marketing2Lead3Title') }}</h2>
<p class="description">{{ $t('marketing2Lead3') }}</p> <p class="description">
{{ $t('marketing2Lead3') }}
</p>
</div> </div>
</div> </div>
<hr> <hr>
@@ -60,12 +76,18 @@
<div class="col-md-6 mb-5 mb-md-0"> <div class="col-md-6 mb-5 mb-md-0">
<img :src="makeUrl('features_mobile.png')"> <img :src="makeUrl('features_mobile.png')">
<h2>{{ $t('marketing3Lead1Title') }}</h2> <h2>{{ $t('marketing3Lead1Title') }}</h2>
<p class="description" v-markdown="$t('marketing3Lead1')"></p> <p
v-markdown="$t('marketing3Lead1')"
class="description"
></p>
</div> </div>
<div class="col-md-6 mb-5 mb-md-0"> <div class="col-md-6 mb-5 mb-md-0">
<img :src="makeUrl('features_opensource.png')"> <img :src="makeUrl('features_opensource.png')">
<h2>{{ $t('marketing3Lead2Title') }}</h2> <h2>{{ $t('marketing3Lead2Title') }}</h2>
<p class="description" v-markdown="$t('marketing3Lead2')"></p> <p
v-markdown="$t('marketing3Lead2')"
class="description"
></p>
</div> </div>
</div> </div>
<hr> <hr>
@@ -80,7 +102,9 @@
<img src="@/assets/images/marketing/education.png"> <img src="@/assets/images/marketing/education.png">
<div class="media-body"> <div class="media-body">
<h2>{{ $t('marketing4Lead1Title') }}</h2> <h2>{{ $t('marketing4Lead1Title') }}</h2>
<p class="description">{{ $t('marketing4Lead1') }}</p> <p class="description">
{{ $t('marketing4Lead1') }}
</p>
</div> </div>
</div> </div>
</div> </div>
@@ -89,7 +113,9 @@
<img src="@/assets/images/marketing/wellness.png"> <img src="@/assets/images/marketing/wellness.png">
<div class="media-body"> <div class="media-body">
<h2>{{ $t('marketing4Lead2Title') }}</h2> <h2>{{ $t('marketing4Lead2Title') }}</h2>
<p class="description">{{ $t('marketing4Lead2') }}</p> <p class="description">
{{ $t('marketing4Lead2') }}
</p>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,9 +1,18 @@
import axios from 'axios'; import axios from 'axios';
import moment from 'moment'; import moment from 'moment';
export const LOCALSTORAGE_AUTH_KEY = 'habit-mobile-settings';
export function authAsCredentialsState (authObject) {
return {
API_ID: authObject.auth.apiId,
API_TOKEN: authObject.auth.apiToken,
};
}
export function setUpAxios (AUTH_SETTINGS) { // eslint-disable-line import/prefer-default-export export function setUpAxios (AUTH_SETTINGS) { // eslint-disable-line import/prefer-default-export
if (!AUTH_SETTINGS) { if (!AUTH_SETTINGS) {
AUTH_SETTINGS = localStorage.getItem('habit-mobile-settings'); // eslint-disable-line no-param-reassign, max-len AUTH_SETTINGS = localStorage.getItem(LOCALSTORAGE_AUTH_KEY); // eslint-disable-line no-param-reassign, max-len
if (!AUTH_SETTINGS) return false; if (!AUTH_SETTINGS) return false;
AUTH_SETTINGS = JSON.parse(AUTH_SETTINGS); // eslint-disable-line no-param-reassign AUTH_SETTINGS = JSON.parse(AUTH_SETTINGS); // eslint-disable-line no-param-reassign
} }

View File

@@ -1,6 +1,8 @@
import { LOCALSTORAGE_AUTH_KEY } from '@/libs/auth';
// @TODO: I have abstracted this in another PR. Use that function when merged // @TODO: I have abstracted this in another PR. Use that function when merged
function getApiKey () { function getApiKey () {
let AUTH_SETTINGS = localStorage.getItem('habit-mobile-settings'); let AUTH_SETTINGS = localStorage.getItem(LOCALSTORAGE_AUTH_KEY);
if (AUTH_SETTINGS) { if (AUTH_SETTINGS) {
AUTH_SETTINGS = JSON.parse(AUTH_SETTINGS); AUTH_SETTINGS = JSON.parse(AUTH_SETTINGS);

View File

@@ -11,7 +11,7 @@
class="balance-info" class="balance-info"
:currency-needed="currencyNeeded" :currency-needed="currencyNeeded"
:amount-needed="amountNeeded" :amount-needed="amountNeeded"
:neededCurrencyOnly="true" :needed-currency-only="true"
/> />
</div> </div>
</template> </template>

View File

@@ -158,15 +158,19 @@ export default {
confirmPassword: this.passwordUpdates.confirmPassword, confirmPassword: this.passwordUpdates.confirmPassword,
}; };
await axios.put('/api/v4/user/auth/update-password', localAuthData); const updatePasswordResult = await axios.put('/api/v4/user/auth/update-password', localAuthData);
const newToken = updatePasswordResult.data.data.apiToken;
this.$store.dispatch('auth:setNewToken', {
userId: this.user._id,
apiToken: newToken,
});
this.passwordUpdates = {}; this.passwordUpdates = {};
this.$store.dispatch('snackbars:add', { // Store a flag to show success message after reload
title: 'Habitica', sessionStorage.setItem('passwordChangeSuccess', 'true');
text: this.$t('passwordSuccess'), window.location.reload();
type: 'success',
timeout: true,
});
}); });
}, },

View File

@@ -147,8 +147,6 @@ import {
const bugReportModal = () => import('@/components/bugReportModal'); const bugReportModal = () => import('@/components/bugReportModal');
const bugReportSuccessModal = () => import('@/components/bugReportSuccessModal'); const bugReportSuccessModal = () => import('@/components/bugReportSuccessModal');
const COMMUNITY_MANAGER_EMAIL = import.meta.env.EMAILS_COMMUNITY_MANAGER_EMAIL;
export default { export default {
name: 'App', name: 'App',
components: { components: {
@@ -325,29 +323,6 @@ export default {
if (loadingScreen) document.body.removeChild(loadingScreen); if (loadingScreen) document.body.removeChild(loadingScreen);
}, },
methods: { methods: {
checkForBannedUser (error) {
const AUTH_SETTINGS = localStorage.getItem('habit-mobile-settings');
const parseSettings = JSON.parse(AUTH_SETTINGS);
const errorMessage = error.response.data.message;
// Case where user is not logged in
if (!parseSettings) {
return false;
}
const bannedMessage = this.$t('accountSuspended', {
communityManagerEmail: COMMUNITY_MANAGER_EMAIL,
userId: parseSettings.auth.apiId,
});
if (errorMessage !== bannedMessage) return false;
this.$store.dispatch('auth:logout', { redirectToLogin: true });
return true;
},
itemSelected (item) {
this.selectedItemToBuy = item;
},
genericPurchase (item) { genericPurchase (item) {
if (!item) return false; if (!item) return false;

View File

@@ -19,16 +19,12 @@ const HallPage = () => import(/* webpackChunkName: "hall" */'@/components/hall/i
const PatronsPage = () => import(/* webpackChunkName: "hall" */'@/components/hall/patrons'); const PatronsPage = () => import(/* webpackChunkName: "hall" */'@/components/hall/patrons');
const HeroesPage = () => import(/* webpackChunkName: "hall" */'@/components/hall/heroes'); const HeroesPage = () => import(/* webpackChunkName: "hall" */'@/components/hall/heroes');
// Admin Panel // Admin Pages
const AdminPanelPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin-panel'); const AdminContainerPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin/container');
const AdminPanelUserPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin-panel/user-support'); const AdminPanelPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin/admin-panel');
const AdminPanelSearchPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin-panel/search'); const AdminPanelUserPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin/admin-panel/user-support');
const AdminPanelSearchPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin/admin-panel/search');
// Except for tasks that are always loaded all the other main level const BlockerPage = () => import(/* webpackChunkName: "admin-panel" */'@/components/admin/blocker');
// All the main level
// components are loaded in separate webpack chunks.
// See https://webpack.js.org/guides/code-splitting-async/
// for docs
// Tasks // Tasks
const UserTasks = () => import(/* webpackChunkName: "userTasks" */'@/components/tasks/user'); const UserTasks = () => import(/* webpackChunkName: "userTasks" */'@/components/tasks/user');
@@ -183,9 +179,20 @@ const router = new VueRouter({
], ],
}, },
{
name: 'adminSection',
path: '/admin',
component: AdminContainerPage,
meta: {
privilegeNeeded: [ // any one of these is enough to give access
'userSupport',
'accessControl',
],
},
children: [
{ {
name: 'adminPanel', name: 'adminPanel',
path: '/admin-panel', path: 'panel',
component: AdminPanelPage, component: AdminPanelPage,
meta: { meta: {
privilegeNeeded: [ // any one of these is enough to give access privilegeNeeded: [ // any one of these is enough to give access
@@ -215,6 +222,18 @@ const router = new VueRouter({
}, },
], ],
}, },
{
name: 'blockers',
path: 'blockers',
component: BlockerPage,
meta: {
privilegeNeeded: [ // any one of these is enough to give access
'accessControl',
],
},
},
],
},
// Only used to handle some redirects // Only used to handle some redirects
// See router.beforeEach // See router.beforeEach

View File

@@ -1,6 +1,20 @@
import axios from 'axios'; import axios from 'axios';
import { authAsCredentialsState, LOCALSTORAGE_AUTH_KEY } from '@/libs/auth';
const LOCALSTORAGE_AUTH_KEY = 'habit-mobile-settings'; function saveLocalDataAuth (store, apiId, apiToken) {
const credentialsObj = {
auth: {
apiId,
apiToken,
},
};
const userLocalData = JSON.stringify(credentialsObj);
localStorage.setItem(LOCALSTORAGE_AUTH_KEY, userLocalData);
store.state.credentials = authAsCredentialsState(credentialsObj);
}
export async function register (store, params) { export async function register (store, params) {
let url = '/api/v4/user/auth/local/register'; let url = '/api/v4/user/auth/local/register';
@@ -16,13 +30,7 @@ export async function register (store, params) {
const user = result.data.data; const user = result.data.data;
const userLocalData = JSON.stringify({ saveLocalDataAuth(store, user.id, user.apiToken);
auth: {
apiId: user._id,
apiToken: user.apiToken,
},
});
localStorage.setItem(LOCALSTORAGE_AUTH_KEY, userLocalData);
} }
export async function login (store, params) { export async function login (store, params) {
@@ -35,14 +43,7 @@ export async function login (store, params) {
const user = result.data.data; const user = result.data.data;
const userLocalData = JSON.stringify({ saveLocalDataAuth(store, user.id, user.apiToken);
auth: {
apiId: user.id,
apiToken: user.apiToken,
},
});
localStorage.setItem(LOCALSTORAGE_AUTH_KEY, userLocalData);
} }
export async function verifyUsername (store, params) { export async function verifyUsername (store, params) {
@@ -72,14 +73,7 @@ export async function socialAuth (store, params) {
const user = result.data.data; const user = result.data.data;
const userLocalData = JSON.stringify({ saveLocalDataAuth(store, user.id, user.apiToken);
auth: {
apiId: user.id,
apiToken: user.apiToken,
},
});
localStorage.setItem(LOCALSTORAGE_AUTH_KEY, userLocalData);
} }
export async function appleAuth (store, params) { export async function appleAuth (store, params) {
@@ -93,14 +87,7 @@ export async function appleAuth (store, params) {
const user = result.data.data; const user = result.data.data;
const userLocalData = JSON.stringify({ saveLocalDataAuth(store, user.id, user.apiToken);
auth: {
apiId: user.id,
apiToken: user.apiToken,
},
});
localStorage.setItem(LOCALSTORAGE_AUTH_KEY, userLocalData);
} }
export function logout (store, options = {}) { export function logout (store, options = {}) {
@@ -108,3 +95,7 @@ export function logout (store, options = {}) {
const query = options.redirectToLogin === true ? '?redirectToLogin=true' : ''; const query = options.redirectToLogin === true ? '?redirectToLogin=true' : '';
window.location.href = `/logout-server${query}`; window.location.href = `/logout-server${query}`;
} }
export function setNewToken (store, params) {
saveLocalDataAuth(store, params.userId, params.apiToken);
}

View File

@@ -0,0 +1,19 @@
import axios from 'axios';
export async function getBlockers () {
const response = await axios.get('/api/v4/admin/blockers');
return response.data.data;
}
export async function createBlocker (store, payload) {
const response = await axios.post('/api/v4/admin/blockers', payload.blocker);
return response.data.data;
}
export async function updateBlocker (store, payload) {
const response = await axios.put(`/api/v4/admin/blockers/${payload.blocker._id}`, payload.blocker);
return response.data.data;
}
export async function deleteBlocker (store, payload) {
const response = await axios.delete(`/api/v4/admin/blockers/${payload.blockerId}`);
return response.data.data;
}

View File

@@ -20,6 +20,7 @@ import * as worldState from './worldState';
import * as news from './news'; import * as news from './news';
import * as analytics from './analytics'; import * as analytics from './analytics';
import * as faq from './faq'; import * as faq from './faq';
import * as blockers from './blockers';
// Actions should be named as 'actionName' and can be accessed as 'namespace:actionName' // Actions should be named as 'actionName' and can be accessed as 'namespace:actionName'
// Example: fetch in user.js -> 'user:fetch' // Example: fetch in user.js -> 'user:fetch'
@@ -45,6 +46,7 @@ const actions = flattenAndNamespace({
news, news,
analytics, analytics,
faq, faq,
blockers,
}); });
export default actions; export default actions;

View File

@@ -6,7 +6,7 @@ import { DAY_MAPPING } from '@/../../common/script/cron';
import deepFreeze from '@/libs/deepFreeze'; import deepFreeze from '@/libs/deepFreeze';
import Store from '@/libs/store'; import Store from '@/libs/store';
import { asyncResourceFactory } from '@/libs/asyncResource'; import { asyncResourceFactory } from '@/libs/asyncResource';
import { setUpAxios } from '@/libs/auth'; import { authAsCredentialsState, LOCALSTORAGE_AUTH_KEY, setUpAxios } from '@/libs/auth';
import actions from './actions'; import actions from './actions';
import getters from './getters'; import getters from './getters';
@@ -22,7 +22,7 @@ const browserTimezoneUtcOffset = moment().utcOffset();
axios.defaults.headers.common['x-client'] = 'habitica-web'; axios.defaults.headers.common['x-client'] = 'habitica-web';
let AUTH_SETTINGS = window.localStorage.getItem('habit-mobile-settings'); let AUTH_SETTINGS = window.localStorage.getItem(LOCALSTORAGE_AUTH_KEY);
if (AUTH_SETTINGS) { if (AUTH_SETTINGS) {
AUTH_SETTINGS = JSON.parse(AUTH_SETTINGS); AUTH_SETTINGS = JSON.parse(AUTH_SETTINGS);
isUserLoggedIn = setUpAxios(AUTH_SETTINGS); isUserLoggedIn = setUpAxios(AUTH_SETTINGS);
@@ -64,10 +64,7 @@ export default function clientStore () {
// see https://github.com/HabitRPG/habitica/issues/9242 // see https://github.com/HabitRPG/habitica/issues/9242
notificationsRemoved: [], notificationsRemoved: [],
worldState: asyncResourceFactory(), worldState: asyncResourceFactory(),
credentials: isUserLoggedIn ? { credentials: isUserLoggedIn ? authAsCredentialsState(AUTH_SETTINGS) : {},
API_ID: AUTH_SETTINGS.auth.apiId,
API_TOKEN: AUTH_SETTINGS.auth.apiToken,
} : {},
// store the timezone offset in case it's different than the one in // store the timezone offset in case it's different than the one in
// user.preferences.timezoneOffset and change it after the user is synced // user.preferences.timezoneOffset and change it after the user is synced
// in app.vue // in app.vue

View File

@@ -55,7 +55,6 @@
"APIToken": "Жетон за ППИ (Това е на практика парола — вижте предупреждението по-горе!)", "APIToken": "Жетон за ППИ (Това е на практика парола — вижте предупреждението по-горе!)",
"showAPIToken": "Показване на жетона за ППИ", "showAPIToken": "Показване на жетона за ППИ",
"hideAPIToken": "Скриване на жетона за ППИ", "hideAPIToken": "Скриване на жетона за ППИ",
"APITokenWarning": "Ако Ви трябва нов жетон за ППИ (ако например старият Ви вече не е таен), пишете на <%= hrefTechAssistanceEmail %>, посочвайки потребителския си идентификатор и текущия си жетон. След като той бъде подновен, ще трябва отново да разрешите достъпа на всичко, което сте използвали, като излезете от профила си в уеб сайта и мобилното приложение, и след това въведете новия си жетон във всички инструменти за Хабитика, които използвате.",
"thirdPartyApps": "Външни приложения", "thirdPartyApps": "Външни приложения",
"resetDo": "Потвърждавам, нулирайте профила ми!", "resetDo": "Потвърждавам, нулирайте профила ми!",
"resetComplete": "Нулирането е завършено!", "resetComplete": "Нулирането е завършено!",

View File

@@ -55,7 +55,6 @@
"APIToken": "API token (toto je heslo - přečti si upozornění nahoře!)", "APIToken": "API token (toto je heslo - přečti si upozornění nahoře!)",
"showAPIToken": "Ukázat API Token", "showAPIToken": "Ukázat API Token",
"hideAPIToken": "Skrýt API Token", "hideAPIToken": "Skrýt API Token",
"APITokenWarning": "Jestli potřebuješ nový API Token (např., pokud jsi jej omylem sdílel), napiš email na <%= hrefTechAssistanceEmail %> obsahující tvé Uživatelské ID a aktuální Token. Jakmile se Token resetuje, budeš potřebovat znovu autorizovat vše pomocí odhlášení se ze stránky a mobilní appky, a poté poskytnout nový Token jakémukoliv jinému Habitica nástroji, který používáš.",
"thirdPartyApps": "Programy třetí strany", "thirdPartyApps": "Programy třetí strany",
"resetDo": "Udělej to, zresetuj můj účet!", "resetDo": "Udělej to, zresetuj můj účet!",
"resetComplete": "Reset dokončen!", "resetComplete": "Reset dokončen!",

View File

@@ -55,7 +55,6 @@
"APIToken": "API-nøgle (det er et kodeord - se advarsel ovenfor!)", "APIToken": "API-nøgle (det er et kodeord - se advarsel ovenfor!)",
"showAPIToken": "Vis API-nøgle", "showAPIToken": "Vis API-nøgle",
"hideAPIToken": "Skjul API-nøgle", "hideAPIToken": "Skjul API-nøgle",
"APITokenWarning": "Hvis du har brug for en ny API-nøgle (f.eks. hvis du har delt den ved et uheld), så send en e-mail til <%= hrefTechAssistanceEmail %> med dit Bruger-ID og din nuværende Nøgle. Når den er nulstillet, skal du godkende alt på ny ved at logge ud af hjemmesiden og mobil-appen, og forsyne alle dine andre Habitica-værktøjer med den nye nøgle.",
"thirdPartyApps": "Tredjeparts-apps", "thirdPartyApps": "Tredjeparts-apps",
"resetDo": "Gør det, nulstil min konto!", "resetDo": "Gør det, nulstil min konto!",
"resetComplete": "Nulstilling gennemført!", "resetComplete": "Nulstilling gennemført!",

View File

@@ -906,5 +906,11 @@
"backgroundTrailThroughAForestNotes": "Wandere einen Weg Durch Einen Wald entlang.", "backgroundTrailThroughAForestNotes": "Wandere einen Weg Durch Einen Wald entlang.",
"backgrounds062025": "SET 133: Veröffentlicht im Juni 2025", "backgrounds062025": "SET 133: Veröffentlicht im Juni 2025",
"backgroundSummerSeashoreText": "Sommer Meeresstrand", "backgroundSummerSeashoreText": "Sommer Meeresstrand",
"backgroundSummerSeashoreNotes": "Erwisch eine Welle an einem Sommer Meeresstrand." "backgroundSummerSeashoreNotes": "Erwisch eine Welle an einem Sommer Meeresstrand.",
"backgrounds072025": "SET 134: Veröffentlicht im Juli 2025",
"backgroundSirensLairText": "Höhle der Sirene",
"backgroundSirensLairNotes": "Wage es, in die Höhle einer Sirene zu tauchen.",
"backgrounds082025": "SET 135: Veröffentlicht im August 2025",
"backgroundSunnyStreetWithShopsText": "Sonnige Straße mit Läden",
"backgroundSunnyStreetWithShopsNotes": "Genieße den Anblick und die Geräusche einer sonnigen Straße mit Läden."
} }

View File

@@ -83,7 +83,7 @@
"allocatePerPop": "Erhöhe Wahrnehmung um einen Punkt", "allocatePerPop": "Erhöhe Wahrnehmung um einen Punkt",
"allocateInt": "Zugewiesene Intelligenzpunkte:", "allocateInt": "Zugewiesene Intelligenzpunkte:",
"allocateIntPop": "Erhöhe Intelligenz um einen Punkt", "allocateIntPop": "Erhöhe Intelligenz um einen Punkt",
"noMoreAllocate": "Jetzt, nach dem Erreichen von Level 100, wirst Du keine weiteren Attributpunkte erhalten. Du kannst weiterspielen, oder ein neues Abenteuer auf Level 1 anfangen, in dem Du die <a href='http://habitica.fandom.com/wiki/Orb_of_Rebirth' target='_blank'>Sphäre der Wiedergeburt</a> benutzt!", "noMoreAllocate": "Jetzt, nach dem Erreichen von Level 100, wirst Du keine weiteren Attributpunkte erhalten. Du kannst weiter aufsteigen oder ein neues Abenteuer auf Level 1 anfangen, indem Du die <a href='/shops/market'>Sphäre der Wiedergeburt</a> benutzt!",
"stats": "Attributwerte", "stats": "Attributwerte",
"strength": "Stärke", "strength": "Stärke",
"strText": "Stärke erhöht die Wahrscheinlichkeit zufälliger \"kritischer Treffer\" und die Rate mit der durch sie Gold, Beute und Erfahrung gewonnen wird. Und hilft auch dabei, Boss-Monstern Schaden zuzufügen.", "strText": "Stärke erhöht die Wahrscheinlichkeit zufälliger \"kritischer Treffer\" und die Rate mit der durch sie Gold, Beute und Erfahrung gewonnen wird. Und hilft auch dabei, Boss-Monstern Schaden zuzufügen.",
@@ -129,8 +129,8 @@
"healerText": "Heiler stehen Schaden unbeeindruckt gegenüber und erweitern diesen Schutz auf Andere. Verpasste Tagesaufgaben und schlechte Angewohnheiten schaden ihnen nicht viel und sie haben Mittel und Wege Lebenspunkte wiederherzustellen. Spiele einen Heiler, wenn Du gerne Anderen in einer Party hilfst, oder wenn es Dich besonders reizt, dem Tod durch harte Arbeit zu entkommen!", "healerText": "Heiler stehen Schaden unbeeindruckt gegenüber und erweitern diesen Schutz auf Andere. Verpasste Tagesaufgaben und schlechte Angewohnheiten schaden ihnen nicht viel und sie haben Mittel und Wege Lebenspunkte wiederherzustellen. Spiele einen Heiler, wenn Du gerne Anderen in einer Party hilfst, oder wenn es Dich besonders reizt, dem Tod durch harte Arbeit zu entkommen!",
"optOutOfClasses": "Später entscheiden", "optOutOfClasses": "Später entscheiden",
"chooseClass": "Wähle Deine Klasse", "chooseClass": "Wähle Deine Klasse",
"chooseClassLearnMarkdown": "[Erfahre mehr über Habiticas Klassensystem](https://habitica.fandom.com/de/wiki/Klassen)", "chooseClassLearnMarkdown": "[Erfahre mehr über Habiticas Klassensystem](/static/faq#what-classes)",
"optOutOfClassesText": "Keine Lust auf Klassen oder Du möchtest Dich später entscheiden? Brich jetzt ab und werde ein Krieger ohne Spezialfähigkeiten. Du kannst Dich im Wiki über das Klassensystem informieren und Deine Klasse jederzeit unter Benutzer Icon -> Statuswerte aktivieren.", "optOutOfClassesText": "Noch nicht bereit zu wählen? Keine Eile! Wenn du ablehnst, kannst du zu jeder Klasse in <a href='/static/faq#what-classes' target='_blank'>unseren FAQ</a> nachlesen und in den Einstellungen das Klassen-System aktivieren, wenn du bereit bist.",
"selectClass": "Wähle <%= heroClass %>", "selectClass": "Wähle <%= heroClass %>",
"select": "Auswählen", "select": "Auswählen",
"stealth": "Schleichen", "stealth": "Schleichen",
@@ -184,8 +184,8 @@
"chatCastSpellUser": "<%= username %> wendet <%= spell %> auf <%= target %> an.", "chatCastSpellUser": "<%= username %> wendet <%= spell %> auf <%= target %> an.",
"purchasePetItemConfirm": "Dieser Einkauf würde die Anzahl der Gegenstände überschreiten, die Du zum Schlüpfen aller möglichen <%= itemText %>-Tiere benötigst. Bist du sicher?", "purchasePetItemConfirm": "Dieser Einkauf würde die Anzahl der Gegenstände überschreiten, die Du zum Schlüpfen aller möglichen <%= itemText %>-Tiere benötigst. Bist du sicher?",
"notEnoughGold": "Nicht genügend Gold.", "notEnoughGold": "Nicht genügend Gold.",
"chatCastSpellPartyTimes": "<%= username %> verwendet <%= spell %> <%= times %> Male für Deine Party <%= times %>.", "chatCastSpellPartyTimes": "<%= username %> wendet <%= spell %> <%= times %> mal für Deine Party an.",
"chatCastSpellUserTimes": "<%= username %> spricht <%= times %> mal <%= spell %> auf <%= target %>.", "chatCastSpellUserTimes": "<%= username %> wendet <%= times %> mal <%= spell %> auf <%= target %> an.",
"nextReward": "Nächste Anmelde-Belohnung", "nextReward": "Nächste Anmelde-Belohnung",
"skins": "Hautfarben", "skins": "Hautfarben",
"titleHaircolor": "Haarfarben", "titleHaircolor": "Haarfarben",

View File

@@ -40,10 +40,10 @@
"backerTier": "Trägerstufe", "backerTier": "Trägerstufe",
"playerTiers": "Spielerstufen", "playerTiers": "Spielerstufen",
"tier": "Level", "tier": "Level",
"conRewardsURL": "https://habitica.fandom.com/wiki/Contributor_Rewards", "conRewardsURL": "https://github.com/HabitRPG/habitica/wiki/Contributing-to-Habitica#contributor-tier-rewards",
"surveysSingle": "Half Habitica zu wachsen, entweder durch das Ausfüllen einer Umfrage oder durch große Hilfe beim Testen. Danke!", "surveysSingle": "Half Habitica zu wachsen, entweder durch das Ausfüllen einer Umfrage oder durch große Hilfe beim Testen. Danke!",
"surveysMultiple": "Hat Habitica an <%= count %> Gelegenheiten geholfen zu wachsen, entweder durch Ausfüllen eines Fragebogens oder durch große Hilfe beim Testen. Danke!", "surveysMultiple": "Hat Habitica an <%= count %> Gelegenheiten geholfen zu wachsen, entweder durch Ausfüllen eines Fragebogens oder durch große Hilfe beim Testen. Danke!",
"blurbHallPatrons": "Dies ist die Halle der Schirmherren, in der wir die edlen Abenteurer ehren, die Habiticas ursprüngliche Kickstarter-Kampagne unterstützt haben. Wir danken ihnen für die Hilfe Habitica zum Leben zu erwecken!", "blurbHallPatrons": "Dies ist die Halle der Schirmherren, in der wir die edlen Abenteurer ehren, die Habiticas ursprüngliche Kickstarter-Kampagne unterstützt haben. Wir danken ihnen für die Hilfe Habitica zum Leben zu erwecken!",
"blurbHallContributors": "Dies ist die Halle der Mitwirkenden, in der Open-Source-Unterstützer von Habitica geehrt werden. Durch Code, Kunst, Musik, Schreiben, oder auch nur Hilfsbereitschaft haben sie <a href='https://habitica.fandom.com/wiki/Contributor_Rewards' target='_blank'> Edelsteine, exklusive Ausrüstung</a> und <a href='https://habitica.fandom.com/wiki/Contributor_Titles' target='_blank'>angesehene Titel</a> verdient. Auch Du kannst Habitica unterstützen! <a href='https://habitica.fandom.com/wiki/Contributing_to_Habitica' target='_blank'> Hier erfährst Du mehr dazu.</a>", "blurbHallContributors": "Dies ist die Halle der Mitwirkenden, in der Open-Source-Unterstützer von Habitica geehrt werden. Durch Code, Kunst, Musik, Schreiben, oder auch nur Hilfsbereitschaft haben sie <a href='https://github.com/HabitRPG/habitica/wiki/Contributing-to-Habitica#contributor-tier-rewards' target='_blank'> Edelsteine, exklusive Ausrüstung</a> und <a href='https://github.com/HabitRPG/habitica/wiki/Contributing-to-Habitica#contributor-tiers' target='_blank'>angesehene Titel</a> verdient. Auch Du kannst Habitica unterstützen! <a href='https://github.com/HabitRPG/habitica/wiki/Contributing-to-Habitica' target='_blank'> Hier erfährst Du mehr dazu.</a>",
"noPrivAccess": "Du besitzt nicht die erforderlichen Berechtigungen." "noPrivAccess": "Du besitzt nicht die erforderlichen Berechtigungen."
} }

View File

@@ -11,7 +11,7 @@
"webFaqAnswer31": "Wenn du eine Aufgabe erfüllst und HP verlierst, obwohl du das nicht hättest tun sollen, kam es zu einer Verzögerung, während der Server die auf anderen Plattformen vorgenommenen Änderungen synchronisiert hat. Wenn du zum Beispiel Gold oder Mana verwendest oder HP in der mobilen App verlierst und dann eine Aufgabe auf der Website erledigst, bestätigt der Server lediglich, dass alles synchronisiert ist.", "webFaqAnswer31": "Wenn du eine Aufgabe erfüllst und HP verlierst, obwohl du das nicht hättest tun sollen, kam es zu einer Verzögerung, während der Server die auf anderen Plattformen vorgenommenen Änderungen synchronisiert hat. Wenn du zum Beispiel Gold oder Mana verwendest oder HP in der mobilen App verlierst und dann eine Aufgabe auf der Website erledigst, bestätigt der Server lediglich, dass alles synchronisiert ist.",
"webFaqAnswer49": "Wenn Du Habitica mit anderen erleben möchtest, aber keine anderen Spieler kennst, ist die Suche nach einer Party die beste Option! Wenn Du bereits andere Spieler kennst, die eine Party haben, kannst Du deinen @Benutzernamen mit ihnen teilen, um eingeladen zu werden. Alternativ kannst Du auch eine neue Gruppe erstellen und sie mit ihrem @Nutzernamen oder ihrer E-Mail-Adresse einladen.\n\nUm eine Party zu erstellen oder zu suchen, wähle \"Party\" im Navigationsmenü und wähle dann die Option, die Dir am besten gefällt.", "webFaqAnswer49": "Wenn Du Habitica mit anderen erleben möchtest, aber keine anderen Spieler kennst, ist die Suche nach einer Party die beste Option! Wenn Du bereits andere Spieler kennst, die eine Party haben, kannst Du deinen @Benutzernamen mit ihnen teilen, um eingeladen zu werden. Alternativ kannst Du auch eine neue Gruppe erstellen und sie mit ihrem @Nutzernamen oder ihrer E-Mail-Adresse einladen.\n\nUm eine Party zu erstellen oder zu suchen, wähle \"Party\" im Navigationsmenü und wähle dann die Option, die Dir am besten gefällt.",
"webFaqAnswer62": "Gruppenpläne bieten dir die einzigartige Möglichkeit, anderen Mitgliedern deines Gruppenplans gemeinsame Aufgaben zuzuweisen. Wenn eine gemeinsame Aufgabe einem Mitglied zugewiesen wird, können andere Mitglieder sie nicht mehr erledigen.\n\nDu kannst eine Aufgabe auch mehreren Mitgliedern zuweisen. Wenn sich zum Beispiel alle Mitglieder die Zähne putzen müssen, erstellst du eine Aufgabe und weist sie jedem Mitglied zu. Jedes Mitglied kann die Aufgabe erledigen und sich seine individuelle Belohnung verdienen. Die Hauptaufgabe wird als erledigt angezeigt, sobald alle Mitglieder sie erledigt haben.", "webFaqAnswer62": "Gruppenpläne bieten dir die einzigartige Möglichkeit, anderen Mitgliedern deines Gruppenplans gemeinsame Aufgaben zuzuweisen. Wenn eine gemeinsame Aufgabe einem Mitglied zugewiesen wird, können andere Mitglieder sie nicht mehr erledigen.\n\nDu kannst eine Aufgabe auch mehreren Mitgliedern zuweisen. Wenn sich zum Beispiel alle Mitglieder die Zähne putzen müssen, erstellst du eine Aufgabe und weist sie jedem Mitglied zu. Jedes Mitglied kann die Aufgabe erledigen und sich seine individuelle Belohnung verdienen. Die Hauptaufgabe wird als erledigt angezeigt, sobald alle Mitglieder sie erledigt haben.",
"webFaqAnswer32": "Alle Spieler beginnen in der Klasse des Kriegers, bis sie Stufe 10 erreicht haben. Sobald du Stufe 10 erreichst, hast du die Wahl, eine neue Klasse zu wählen oder als Krieger weiterzuspielen.\n\nJede Klasse verfügt über unterschiedliche Ausrüstungen und Fertigkeiten. Wenn du dich nicht für eine Klasse entscheiden möchtest, kannst du \"Abbrechen\" wählen. Wenn du dich später doch entscheidest, kannst du das Klassensystem in den Einstellungen wieder aktivieren.\n\nWenn Du Deine Klasse nach Level 10 noch einmal ändern möchtest, kannst Du die Sphäre der Wiedergeburt hierfür nutzen. Die Sphäre der Wiedergeburt ist mit Level 50 auf demMarktplatz für 6 Edelsteine verfügbar und mit Level 100 bekommst Du sie umsonst.\n\nAlternativ kannst Du Deine Klasse jederzeit in den Einstellungen für 3 Edelsteine ändern. Dies wird Dein Level nicht wie die Sphäre der Wiedergeburt zurücksetzen, aber es erlaubt Dir, die Fertigkeits-Punkte, die Du beim Leveln gesammelt hast, Deiner neuen Klasse zuzuordnen.", "webFaqAnswer32": "Alle Spieler beginnen in der Klasse des Kriegers, bis sie Stufe 10 erreicht haben. Sobald du Stufe 10 erreichst, hast du die Wahl, eine neue Klasse zu wählen oder als Krieger weiterzuspielen.\n\nJede Klasse verfügt über unterschiedliche Ausrüstungen und Fertigkeiten. Wenn du dich nicht für eine Klasse entscheiden möchtest, kannst du \"Abbrechen\" wählen. Du kannst das Klassensystem in den Einstellungen jederzeit wieder aktivieren.\n\nWenn du deine Klasse nach Level 10 noch einmal ändern möchtest, kannst du die Sphäre der Wiedergeburt hierfür nutzen. Die Sphäre der Wiedergeburt ist mit Level 50 auf dem Marktplatz für 6 Edelsteine verfügbar und mit Level 100 bekommst du sie umsonst.\n\nAlternativ kannst du deine Klasse jederzeit in den Einstellungen für 3 Edelsteine ändern. Dies wird dein Level nicht wie die Sphäre der Wiedergeburt zurücksetzen, aber es erlaubt dir, die Fähigkeitspunkte, die du beim Leveln gesammelt hast, deiner neuen Klasse zuzuordnen.",
"sunsetFaqPara14": "<strong>Linguists</strong><br />Wir freuen uns auch weiterhin über Hilfe bei der Übersetzung der Apps und der Website und werden für qualifizierte Beiträge nach wie vor Beitragsstufen vergeben. Die Methode, mit der wir Übersetzungen annehmen, wird sich jedoch ändern. Wir möchten unsere Ressourcen auf die Unterstützung einer bestimmten Auswahl von Sprachen für alle Plattformen konzentrieren. Um dies zu erreichen, werden wir die Anzahl der für Übersetzungen verfügbaren Sprachen reduzieren. Zuvor nicht fertiggestellte Sprachen werden in Github archiviert. Wir hoffen, dass diese Änderung das plattformübergreifende Habitica-Erlebnis konsistenter macht. Sie können unsere aktuellsten Richtlinien für das Übersetzungsverfahren auf unserer Website lesen <a href='https://translate.habitica.com/projects/habitica/#information'>Übersetzungswebsite</a>.", "sunsetFaqPara14": "<strong>Linguists</strong><br />Wir freuen uns auch weiterhin über Hilfe bei der Übersetzung der Apps und der Website und werden für qualifizierte Beiträge nach wie vor Beitragsstufen vergeben. Die Methode, mit der wir Übersetzungen annehmen, wird sich jedoch ändern. Wir möchten unsere Ressourcen auf die Unterstützung einer bestimmten Auswahl von Sprachen für alle Plattformen konzentrieren. Um dies zu erreichen, werden wir die Anzahl der für Übersetzungen verfügbaren Sprachen reduzieren. Zuvor nicht fertiggestellte Sprachen werden in Github archiviert. Wir hoffen, dass diese Änderung das plattformübergreifende Habitica-Erlebnis konsistenter macht. Sie können unsere aktuellsten Richtlinien für das Übersetzungsverfahren auf unserer Website lesen <a href='https://translate.habitica.com/projects/habitica/#information'>Übersetzungswebsite</a>.",
"webFaqAnswer34": "Haustiere mögen Futter, das zu ihrer Farbe passt. Basis-Tiere sind die Ausnahme, aber alle Basis-Tiere mögen den gleichen Gegenstand. Im Folgenden siehst du, welche Nahrungsmittel jedes Haustier mag:\n\n * Basistiere mögen Fleisch\n * Weiße Haustiere mögen Milch\n * Wüstenhaustiere mögen Kartoffeln\n * Rote Haustiere mögen Erdbeeren\n * Schattentiere mögen Schokolade\n * Skelett-Tiere mögen Fisch\n * Zombie-Tiere mögen verdorbenes Fleisch\n * Zuckerwatte rosa Haustiere mögen rosa Zuckerwatte\n * Zuckerwatte blaue Haustiere mögen blaue Zuckerwatte\n * Goldene Haustiere mögen Honig", "webFaqAnswer34": "Haustiere mögen Futter, das zu ihrer Farbe passt. Basis-Tiere sind die Ausnahme, aber alle Basis-Tiere mögen den gleichen Gegenstand. Im Folgenden siehst du, welche Nahrungsmittel jedes Haustier mag:\n\n * Basistiere mögen Fleisch\n * Weiße Haustiere mögen Milch\n * Wüstenhaustiere mögen Kartoffeln\n * Rote Haustiere mögen Erdbeeren\n * Schattentiere mögen Schokolade\n * Skelett-Tiere mögen Fisch\n * Zombie-Tiere mögen verdorbenes Fleisch\n * Zuckerwatte rosa Haustiere mögen rosa Zuckerwatte\n * Zuckerwatte blaue Haustiere mögen blaue Zuckerwatte\n * Goldene Haustiere mögen Honig",
"webFaqAnswer35": "Sobald du dein Haustier genug gefüttert hast, um es zu einem Reittier zu machen, musst du diese Art von Haustier erneut ausbrüten, um es in deinem Stall zu haben.\n\nUm Reittiere in den mobilen Apps zu sehen:\n\n * Wähle im Menü \"Haustiere & Reittiere\" und wechseln zur Registerkarte \"Reittiere\".\n\nSo zeigst du Reittiere auf der Website an:\n\n * Wähle im Menü \"Inventar\" die Option \"Haustiere und Reittiere\" und scrollen nach unten zum Abschnitt \"Reittiere\"", "webFaqAnswer35": "Sobald du dein Haustier genug gefüttert hast, um es zu einem Reittier zu machen, musst du diese Art von Haustier erneut ausbrüten, um es in deinem Stall zu haben.\n\nUm Reittiere in den mobilen Apps zu sehen:\n\n * Wähle im Menü \"Haustiere & Reittiere\" und wechseln zur Registerkarte \"Reittiere\".\n\nSo zeigst du Reittiere auf der Website an:\n\n * Wähle im Menü \"Inventar\" die Option \"Haustiere und Reittiere\" und scrollen nach unten zum Abschnitt \"Reittiere\"",
@@ -102,7 +102,7 @@
"sunsetFaqPara4": "Um die Zeit zu feiern, die wir erlebt haben, werden wir jedem ein Veteranen-Haustier schenken, während wir uns in diese neue Ära begeben. Für unsere großartigen Mitwirkenden werden wir außerdem ein spezielles Ausrüstungsset verschicken, um ihre harte Arbeit in den Habitica-Communities zu würdigen.", "sunsetFaqPara4": "Um die Zeit zu feiern, die wir erlebt haben, werden wir jedem ein Veteranen-Haustier schenken, während wir uns in diese neue Ära begeben. Für unsere großartigen Mitwirkenden werden wir außerdem ein spezielles Ausrüstungsset verschicken, um ihre harte Arbeit in den Habitica-Communities zu würdigen.",
"sunsetFaqPara6": "Der Service für Tavernen sowie öffentliche und private Gilden endet und diese Räume werden am <strong>August 8, 2023</strong> aus Habitica entfernt.", "sunsetFaqPara6": "Der Service für Tavernen sowie öffentliche und private Gilden endet und diese Räume werden am <strong>August 8, 2023</strong> aus Habitica entfernt.",
"sunsetFaqList1": "Der Hauptzweck von Habitica ist es, Motivation durch ein gamifiziertes Aufgabenmanagement zu schaffen. Gilden und Tavernen werden von einem unverhältnismäßig kleinen Prozentsatz unserer Spielerbasis genutzt. Die Mehrheit der Spieler nutzt externe Dienste, die in erster Linie für soziale Interaktion gedacht sind und absichtlich mit Blick auf diese Anwendungsfälle entwickelt und gepflegt werden.", "sunsetFaqList1": "Der Hauptzweck von Habitica ist es, Motivation durch ein gamifiziertes Aufgabenmanagement zu schaffen. Gilden und Tavernen werden von einem unverhältnismäßig kleinen Prozentsatz unserer Spielerbasis genutzt. Die Mehrheit der Spieler nutzt externe Dienste, die in erster Linie für soziale Interaktion gedacht sind und absichtlich mit Blick auf diese Anwendungsfälle entwickelt und gepflegt werden.",
"sunsetFaqList2": "Die neuen Online-Sicherheitsgesetze erfordern ein Maß an aktiver Inhaltskontrolle für öffentliche Bereiche, das Habitica in der Vergangenheit nicht geboten hat. Die Investition in die Funktionen, die diese neuen Vorschriften erfordern würden, würde dazu führen, dass unsere begrenzten Ressourcen in Teile von Habitica umgeleitet werden, die die große Mehrheit der Spieler nie berührt.", "sunsetFaqList2": "Die neuen Online-Sicherheitsgesetze erfordern ein Maß an aktiver Inhaltskontrolle für öffentliche Bereiche, das Habitica in der Vergangenheit nicht geboten hat. Die Investition in die Funktionen, die diese neuen Vorschriften erfordern würden, würde dazu führen, dass unsere begrenzten Ressourcen in Teile von Habitica umgeleitet werden, welche die große Mehrheit der Spieler nie berührt.",
"sunsetFaqList3": "Es ist uns wichtig, der ständig wachsenden internationalen Spielerbasis von Habitica weiterhin einen weltweiten Zugang zu bieten. Die Entfernung dieser Dienste ermöglicht es uns, dieses Ziel weiter zu verfolgen, ohne den Zugang in Regionen einschränken zu müssen, in denen eine aktivere Kontrolle der Inhalte erforderlich ist, als wir sie bieten können.", "sunsetFaqList3": "Es ist uns wichtig, der ständig wachsenden internationalen Spielerbasis von Habitica weiterhin einen weltweiten Zugang zu bieten. Die Entfernung dieser Dienste ermöglicht es uns, dieses Ziel weiter zu verfolgen, ohne den Zugang in Regionen einschränken zu müssen, in denen eine aktivere Kontrolle der Inhalte erforderlich ist, als wir sie bieten können.",
"sunsetFaqPara7": "Partys und Gruppenpläne werden beibehalten und behalten ihre Chatbereiche. Du wirst auch weiterhin private Nachrichten senden können.", "sunsetFaqPara7": "Partys und Gruppenpläne werden beibehalten und behalten ihre Chatbereiche. Du wirst auch weiterhin private Nachrichten senden können.",
"sunsetFaqPara12": "Als Open-Source-Projekt begrüßen und fördern wir viele Arten von Beiträgen. Um unsere Wertschätzung zu zeigen, werden wir das heroische Ausrüstungsset an alle Spieler schicken, die vor dem <strong>1. August 2023</strong>eine Beitragsstufe haben. Wenn die Tavernen- und Gilden-Dienste enden, wird es auch bei den Beiträgen einige Änderungen geben. Im Folgenden erfährst du mehr über den jeweiligen Plan für die einzelnen Typen.", "sunsetFaqPara12": "Als Open-Source-Projekt begrüßen und fördern wir viele Arten von Beiträgen. Um unsere Wertschätzung zu zeigen, werden wir das heroische Ausrüstungsset an alle Spieler schicken, die vor dem <strong>1. August 2023</strong>eine Beitragsstufe haben. Wenn die Tavernen- und Gilden-Dienste enden, wird es auch bei den Beiträgen einige Änderungen geben. Im Folgenden erfährst du mehr über den jeweiligen Plan für die einzelnen Typen.",
@@ -243,5 +243,5 @@
"subscriptionDetail470": "Gruppenabonnentenvorteile verhalten sich genauso wie die eines wiederkehrenden 1-Monats-Abonnements. Du erhältst eine Mystische Sanduhr am Anfang jedes Monats und die Anzahl an Edelsteinen, die du jeden Monat auf dem Marktplatz kaufen kannst, wird sich erhöhen bis zu einem Limit von 50.", "subscriptionDetail470": "Gruppenabonnentenvorteile verhalten sich genauso wie die eines wiederkehrenden 1-Monats-Abonnements. Du erhältst eine Mystische Sanduhr am Anfang jedes Monats und die Anzahl an Edelsteinen, die du jeden Monat auf dem Marktplatz kaufen kannst, wird sich erhöhen bis zu einem Limit von 50.",
"subscriptionPara3": "Wir hoffen, dass dieser neue Rhythmus besser vorhersagbar ist, mehr Zugang zur fantastischen Gegenstandauswahl im Laden des Zeitreisenden ermöglicht und noch mehr Motivation bietet, jeden Monat Fortschritte an deinen Aufgaben zu machen!", "subscriptionPara3": "Wir hoffen, dass dieser neue Rhythmus besser vorhersagbar ist, mehr Zugang zur fantastischen Gegenstandauswahl im Laden des Zeitreisenden ermöglicht und noch mehr Motivation bietet, jeden Monat Fortschritte an deinen Aufgaben zu machen!",
"faqQuestion67": "Was sind die Klassen in Habitica?", "faqQuestion67": "Was sind die Klassen in Habitica?",
"webFaqAnswer67": "Klassen sind verschiedene Rollen, die dein Charakter spielen kann. Jede Klasse bietet ihre eigene Reihe von einzigartigen Vorteilen und Fähigkeiten beim Aufsteigen auf höhere Level. Diese Fähigkeiten können das Bearbeiten deiner Aufgaben ergänzen oder dabei helfen, deine Party beim Abschließen von Quests zu unterstützen.\n\nDeine Klasse bestimmt auch, welche Ausrüstung für dich in den Belohnungen, im Marktplatz und im Jahreszeitenmarkt zum Kauf erhältlich ist.\n\nHier ist eine Zusammenfassung jeder Klasse, um dir dabei zu helfen, diejenige zu wählen, welche am besten zu deinem Spielstil passt:\n#### **Krieger**\n* Krieger verursachen hohen Schaden bei Bossen und haben eine hohe Chance für kritische Treffer beim Abschließen von Aufgaben, was dich mit extra Erfahrung und Gold belohnt.\n* Stärke ist ihr primäres Attribut, welches den Schaden erhöht, den sie verursachen.\n* Ausdauer ist ihr sekundäres Attribut, welches den Schaden verringert, den sie erhalten.\n* Die Fähigkeiten der Krieger erhöhen die Ausdauer und Stärke der Party Kameraden.\n* Erwäge, einen Krieger zu spielen, wenn du es liebst, Bosse zu bekämpfen und auch ein wenig Schutz möchtest, wenn du gelegentlich Aufgaben versäumst.\n#### **Heiler**\n* Heiler haben eine starke Verteidigung und können sich selbst, sowie die Party Kameraden, heilen.\n* Ausdauer ist ihr primäres Attribut, welches ihre Heilungen verstärkt und den Schaden, den sie erhalten, verringert.\n* Intelligenz ist ihr sekundäres Attribut, welches ihr Mana und ihre Erfahrung erhöht.\n* Die Fähigkeiten der Heiler bewirken, dass ihre Aufgaben weniger rot werden und erhöhen die Ausdauer der Party Kameraden.\n* Erwäge, einen Heiler zu spielen, wenn du oft Aufgaben versäumst, und die Fähigkeit benötigst, dich selbst und deine Party Kameraden zu heilen. Heiler erreichen schnell neue Level.\n#### **Magier**\n* Magier gewinnen schnell neue Level und viel Mana, und verursachen Schaden bei Bossen in Quests.\n* Intelligenz ist ihr primäres Attribut, welches ihr Mana und ihre Erfahrung erhöht.\n* Wahrnehmung ist ihr sekundäres Attribut, welches ihr gefundenes Gold und ihre gefundenen Gegenstände vermehrt.\n* Die Fähigkeiten der Magier bewirken, dass ihre Aufgaben Strähnen eingefroren werden, stellen das Mana ihrer Party Kameraden wieder her, und erhöhen ihre Intelligenz.\n* Erwäge, einen Magier zu spielen, wenn du durch das schnelle Erreichen neuer Level und das Beisteuern von Schaden in Boss Quests motiviert wirst.\n#### **Schurke**\n* Schurken bekommen die meisten erbeuteten Gegenstände und das meiste Gold beim Erledigen von Aufgaben, und haben eine höhere Chance, kritische Treffer zu erzielen, was ihnen noch mehr Erfahrung und Gold beschert.\n* Wahrnehmung ist ihr primäres Attribut, welches ihr gefundenes Gold und ihre gefundenen Gegenstände vermehrt.\n* Stärke ist ihr sekundäres Attribut, welches den Schaden erhöht, den sie verursachen.\n* Die Fähigkeiten der Schurken helfen ihnen, versäumten Tagesaufgaben auszuweichen, Gold zu klauen, und die Wahrnehmung ihrer Party Kameraden zu erhöhen.\n* Erwäge, einen Schurken zu spielen, wenn du durch Belohnungen sehr motiviert wirst." "webFaqAnswer67": "Klassen sind verschiedene Rollen, die dein Charakter spielen kann. Jede Klasse bietet ihre eigene Reihe von einzigartigen Vorteilen und Fähigkeiten beim Aufsteigen auf höhere Level. Diese Fähigkeiten können das Bearbeiten deiner Aufgaben ergänzen oder dabei helfen, deine Party beim Abschließen von Quests zu unterstützen.\n\nDeine Klasse bestimmt auch, welche Ausrüstung für dich in den Belohnungen, im Marktplatz und im Jahreszeitenmarkt zum Kauf erhältlich ist.\n\nHier ist eine Zusammenfassung jeder Klasse, um dir dabei zu helfen, diejenige zu wählen, welche am besten zu deinem Spielstil passt:\n#### **Krieger**\n* Die Krieger verursachen hohen Schaden bei Bossen und haben eine hohe Chance für kritische Treffer beim Abschließen von Aufgaben, was dich mit extra Erfahrung und Gold belohnt.\n* Stärke ist ihr primäres Attribut, welches den Schaden erhöht, den sie verursachen.\n* Ausdauer ist ihr sekundäres Attribut, welches den Schaden verringert, den sie erhalten.\n* Die Fähigkeiten der Krieger erhöhen die Ausdauer und Stärke der Gruppenmitglieder.\n* Erwäge, einen Krieger zu spielen, wenn du es liebst, Bosse zu bekämpfen und auch ein wenig Schutz möchtest, wenn du gelegentlich Aufgaben versäumst.\n#### **Heiler**\n* Die Heiler haben eine starke Verteidigung und können sich selbst, sowie Gruppenmitglieder, heilen.\n* Ausdauer ist ihr primäres Attribut, welches ihre Heilungen verstärkt und den Schaden, den sie erhalten, verringert.\n* Intelligenz ist ihr sekundäres Attribut, welches ihr Mana und ihre Erfahrung erhöht.\n* Die Fähigkeiten der Heiler bewirken, dass ihre Aufgaben weniger rot werden und erhöhen die Ausdauer der Gruppenmitglieder.\n* Erwäge, einen Heiler zu spielen, wenn du oft Aufgaben versäumst, und die Fähigkeit benötigst, dich selbst und deine Gruppenmitglieder zu heilen. Heiler erreichen schnell neue Level.\n#### **Magier**\n* Die Magier gewinnen schnell neue Level und viel Mana, und verursachen Schaden bei Bossen in Quests.\n* Intelligenz ist ihr primäres Attribut, welches ihr Mana und ihre Erfahrung erhöht.\n* Wahrnehmung ist ihr sekundäres Attribut, welches ihr gefundenes Gold und ihre gefundenen Gegenstände vermehrt.\n* Die Fähigkeiten der Magier bewirken, dass ihre Aufgaben Strähnen eingefroren werden, stellen das Mana ihrer Gruppenmitglieder wieder her, und erhöhen ihre Intelligenz.\n* Erwäge, einen Magier zu spielen, wenn du durch das schnelle Erreichen neuer Level und das Beisteuern von Schaden in Boss Quests motiviert wirst.\n#### **Schurke**\n* Die Schurken bekommen die meisten erbeuteten Gegenstände und das meiste Gold beim Erledigen von Aufgaben und haben eine höhere Chance, kritische Treffer zu erzielen, was ihnen noch mehr Erfahrung und Gold beschert.\n* Wahrnehmung ist ihr primäres Attribut, welches ihr gefundenes Gold und ihre gefundenen Gegenstände vermehrt.\n* Stärke ist ihr sekundäres Attribut, welches den Schaden erhöht, den sie verursachen.\n* Die Fähigkeiten der Schurken helfen ihnen, versäumten Tagesaufgaben auszuweichen, Gold zu klauen und die Wahrnehmung ihrer Gruppenmitglieder zu erhöhen.\n* Erwäge, einen Schurken zu spielen, wenn du durch Belohnungen sehr motiviert wirst."
} }

View File

@@ -25,32 +25,32 @@
"invalidEmail": "Um das Passwort zurückzusetzen, ist eine gültige E-Mail-Adresse notwendig.", "invalidEmail": "Um das Passwort zurückzusetzen, ist eine gültige E-Mail-Adresse notwendig.",
"login": "Anmelden", "login": "Anmelden",
"logout": "Abmelden", "logout": "Abmelden",
"marketing1Header": "Verbessere spielerisch Deinen Lebensstil mit Habitica", "marketing1Header": "Verbessere deine Gewohnheiten mit jedem Level!",
"marketing1Lead1Title": "Dein Leben, das Rollenspiel", "marketing1Lead1Title": "Mache dein Leben zum Spiel",
"marketing1Lead1": "Habitica ist ein Computerspiel, welches Dir dabei hilft, Deine Gewohnheiten im realen Leben zu verbessern. Es \"gamifiziert\" Dein Leben, indem es all Deine Aufgaben (Gewohnheiten, Tagesaufgaben und To-Dos) in kleine Monster verwandelt, die Du besiegen musst. Je besser Du Dich dabei anstellst, umso weiter kommst Du im Spiel. Wenn Du in Deinem realen Leben nachlässt, beginnt Dein Charakter im Spiel zurückzufallen.", "marketing1Lead1": "Habitica ist die perfekte App, für alle die Probleme mit ToDo-Listen haben. Wir verwenden bekannte Spiel-Mechaniken wie Belohnungen in Gold, XP und Gegenstände, die dir dabei helfen, dich produktiver zu fühlen und dein Erfolgserlebnis zu steigern, wenn du Aufgaben vollendest. Je besser Du Dich dabei anstellst, umso weiter kommst Du im Spiel.",
"marketing1Lead2Title": "Bekomme coole Ausrüstung", "marketing1Lead2Title": "Rüsten dich mit Stil aus",
"marketing1Lead2": "Verbessere Deine Gewohnheiten, um Deinen Avatar aufzubauen. Gib' ordentlich an mit all der Ausrüstung, die Du verdient hast!", "marketing1Lead2": "Sammele Schwerter, Rüstungen und vieles mehr mit Gold, welches du beim Vollenden von Aufgaben verdienst. Mit hunderten von Ausrüstungsstücken, die du sammeln und auswählen kannst, werden dir nie Kombinationen zum Ausprobieren ausgehen. Optimiere deine Statistik, deinen Style oder beides zusammen! ",
"marketing1Lead3Title": "Finde zufällige Preise", "marketing1Lead3Title": "Verdiene Belohnungen für deine Bemühungen",
"marketing1Lead3": "Für manche ist es das Glücksspiel, was sie motiviert: das System der \"zufälligen Belohnungen\". Habitica kombiniert alle Stile des bestärkenden Lernens: positiv, negativ, erwartet oder zufällig.", "marketing1Lead3": "Etwas zu haben, auf das man sich freuen kann, mag den Unterschied ausmachen, ob man eine Aufgabe erledigt oder ob sie einen wochenlang quält. Wenn das Leben keine Belohnung bietet ist Habitica für dich da! Du wirst für jede Aufgabe belohnt, aber Überraschungen gibt es an jeder Ecke - also mach weiter so! ",
"marketing2Header": "Messe Dich mit Freunden", "marketing2Header": "Schließe dich mit Freunden zusammen",
"marketing2Lead1Title": "Soziale Produktivität", "marketing2Lead1Title": "Soziale Produktivität",
"marketing2Lead1": "Obwohl Du Habitica im Alleingang spielen kannst, wird es erst richtig spaßig, wenn Du anfängst, mit anderen zusammenzuarbeiten, zu wetteifern und einander zur Verantwortung zu ziehen. Der effektivste Teil von allen Persönlichkeitsentwicklungsprogrammen ist die soziale Verantwortlichkeit - und wo gibt es eine bessere Umgebung für Verantwortung und Wettkampf als in einem Videospiel?", "marketing2Lead1": "Hole dir einen Motivationsschub, indem du mit anderen zusammenarbeitest, konkurrierst und interagierst! Habitica wurde entwickelt, um den effektivsten Teil eines jeden Selbstverbesserungsprogramms zu nutzen: soziale Verantwortung.",
"marketing2Lead2Title": "Bezwinge Monster", "marketing2Lead2Title": "Bekämpfe Monster in Quests",
"marketing2Lead2": "Was ist schon ein Rollenspiel ohne Kämpfe? Bezwinge Monster mit Deiner Party. Monster sind der \"super Verantwortlichkeitsmodus\": wenn Du an einem Tag nicht ins Fitnessstudio gehst, dann verletzt das Monster *alle!*", "marketing2Lead2": "Nimm dich einer unserer hunderten von Quests mit einer Gruppe von Freunden an, um dich ins Getümmel zu stürzen. Die Monster der Quests bringen deine Verantwortlichkeit auf die Spitze. Wenn du vergisst, Zahnseide zu benutzen, schadet das allen!",
"marketing2Lead3Title": "Fordert einander heraus", "marketing2Lead3Title": "Fordert einander heraus",
"marketing2Lead3": "Bei Herausforderungen kannst Du Dich mit Freunden und Unbekannten messen. Wer sich bis zum Ende der Herausforderung am besten schlägt, gewinnt besondere Preise.", "marketing2Lead3": "Nimm an Herausforderungen teil, die von unserer Community erstellt wurden und erhalte zusammengestellte Aufgabenlisten, die deinen Interessen und Zielen entsprechen. Gib dein Bestes dabei, um den Edelsteinpreis zu wetteifern, der dem Gewinner verliehen wird!",
"marketing3Header": "Apps und Erweiterungen", "marketing3Header": "Weitere Möglichkeiten, Habitica zu nutzen",
"marketing3Lead1": "Mit den **iPhone & Android** Apps kannst Du alles von Unterwegs erledigen. Wir wissen, dass sich auf einer Webseite einzuloggen, um Knöpfe zu drücken, manchmal anstrengend sein kann.", "marketing3Lead1": "Du kannst Habitica auf deinem Android- oder iOS-Gerät nutzen, um Aufgaben überall abzuhaken. Schau dir unsere preisgekrönten Apps an, um einen neuen Ansatz zur Erledigung von Aufgaben zu finden.",
"marketing3Lead2Title": "Einbindungen", "marketing3Lead2Title": "Open-Source Community",
"marketing3Lead2": "Andere **Tools von Drittanbietern** binden Habitica in unterschiedlichen Bereichen Deines Lebens ein. Unser API stellt eine einfache Verknüpfung z.B. mit [Chrome Extension](https://chrome.google.com/webstore/detail/habitica/pidkmpibnnnhneohdgjclfdjpijggmjj?hl=en-US) her, die es ermöglicht, für das Besuchen unproduktiver Webseiten Punkte zu verlieren oder für das Besuchen produktiver Webseiten Punkte zu erhalten. [Mehr dazu hier](https://habitica.fandom.com/wiki/Extensions,_Add-Ons,_and_Customizations).", "marketing3Lead2": "Wir sind stolz darauf, ein Open-Source-Projekt zu sein, das Beiträge von unserer engagierten Community begrüßt. Passe Habitica deinen eigenen Bedürfnissen an oder trage dazu bei, die Erfahrung aller Spieler auf der ganzen Welt zu verbessern. Besuche uns auf [GitHub](https://github.com/HabitRPG/habitica/wiki/Contributing-to-Habitica), um mehr zu erfahren!",
"marketing4Header": "Verwendung für Organisationen", "marketing4Header": "Mehr als nur Hausarbeit",
"marketing4Lead1": "Bildung ist einer der besten Bereiche für Gamifizierung. Wir wissen alle, wie sehr Schüler*innen dieser Tage an ihren Handys und Spielen hängen; nutze diese Macht! Lasse sie in freundlichen Wettbewerben gegeneinander antreten und belohne gutes Verhalten mit seltenen Preisen. Schaue zu, wie sich ihre Noten und ihr Verhalten verbessern.", "marketing4Lead1": "Unterricht ist einer der besten Bereiche für ein wenig Gamification! Durchbrich die Monotonie der täglichen Klassenarbeit, indem du ein paar Spiele in einbaust. Mit Habitica kannst du auf unterhaltsame Weise die Hausaufgaben verfolgen, Herausforderungen im Klassenzimmer schaffen und deine SchülerInnen mit ihren Erfolgen angeben lassen.",
"marketing4Lead1Title": "Betrachtungswinkel Ausbildung", "marketing4Lead1Title": "Gamification in der Bildung",
"marketing4Lead2": "Die Kosten für medizinische Versorgung steigen und irgendjemand muss sie tragen. Zahlreiche Pläne wurden entwickelt, um Kosten zu reduzieren und das Wohlbefinden zu verbessern. Wir glauben, dass Habitica einen wesentlichen Beitrag zu gesünderen Lebensstilen leisten kann.", "marketing4Lead2": "Der Aufbau eines gesünderen Lebensstils kann leicht zu einem überwältigenden Unterfangen werden. Habitica hilft dir dabei, alle Aspekte deiner Fitnessziele zu verfolgen, mit flexibler Zeitplanung und Intensität, um dich dort abzuholen, wo du gerade bist. Habe also etwas Spaß, während du auf deine Gesundheit hinarbeitest!",
"marketing4Lead2Title": "Betrachtungswinkel Gesundheit und Erholung", "marketing4Lead2Title": "Gamification von Gesundheit und Erholung",
"marketing4Lead3-1": "Willst Du Dein Leben einmal als Spiel betrachten?", "marketing4Lead3-1": "Bereit Spaß zu haben beim Erledigen von Aufgaben?",
"marketing4Lead3-2": "Willst Du eine Gruppe für Ausbildung, Wohlbefinden usw. leiten?", "marketing4Lead3-2": "Willst Du eine Gruppe für Ausbildung, Wohlbefinden usw. leiten?",
"marketing4Lead3Title": "Mache Alles zum Spiel", "marketing4Lead3Title": "Starte deine Reise!",
"mobileAndroid": "Android App", "mobileAndroid": "Android App",
"mobileIOS": "iOS App", "mobileIOS": "iOS App",
"oldNews": "Neuigkeiten", "oldNews": "Neuigkeiten",
@@ -111,7 +111,7 @@
"missingPassword": "Fehlendes Passwort.", "missingPassword": "Fehlendes Passwort.",
"missingNewPassword": "Fehlendes neues Passwort.", "missingNewPassword": "Fehlendes neues Passwort.",
"invalidEmailDomain": "Du kannst E-Mails mit den folgenden Domains nicht registrieren: <%= domains %>", "invalidEmailDomain": "Du kannst E-Mails mit den folgenden Domains nicht registrieren: <%= domains %>",
"wrongPassword": "Das Passwort ist falsch. Wenn Du Dein Passwort vergessen hast, klicke auf „Passwort vergessen“.", "wrongPassword": "Das Passwort ist falsch. Wenn Du Dein Passwort vergessen hast, klicke auf „Passwort vergessen“",
"incorrectDeletePhrase": "Bitte gebe <%= magicWord %> in Großbuchstaben ein, um Dein Konto zu löschen.", "incorrectDeletePhrase": "Bitte gebe <%= magicWord %> in Großbuchstaben ein, um Dein Konto zu löschen.",
"notAnEmail": "Ungültige E-Mail-Adresse.", "notAnEmail": "Ungültige E-Mail-Adresse.",
"emailTaken": "Diese E-Mail-Adresse wird bereits von einem Konto verwendet.", "emailTaken": "Diese E-Mail-Adresse wird bereits von einem Konto verwendet.",
@@ -182,5 +182,6 @@
"incorrectResetPhrase": "Bitte tippe <%= magicWord %> in Großbuchstaben um deinen Account zurückzusetzen.", "incorrectResetPhrase": "Bitte tippe <%= magicWord %> in Großbuchstaben um deinen Account zurückzusetzen.",
"translateHabitica": "Habitica übersetzen", "translateHabitica": "Habitica übersetzen",
"marketing3Lead1Title": "Android & iOS Apps", "marketing3Lead1Title": "Android & iOS Apps",
"marketing4Lead3Button": "Starte noch heute" "marketing4Lead3Button": "Starte noch heute",
"emailBlockedRegistration": "Diese E-Mail ist für die Registrierung blockiert. Wenn du denkst, dass das ein Fehler ist, kontaktiere uns bitte unter admin@habitica.com."
} }

View File

@@ -2485,7 +2485,7 @@
"weaponSpecialWinter2022HealerText": "Kristallklarer Zauberstab aus Eis", "weaponSpecialWinter2022HealerText": "Kristallklarer Zauberstab aus Eis",
"weaponSpecialWinter2022HealerNotes": "Berühre einen Freund mit diesem Werkzeug aus festgefrorenem Wasser am Nacken, und er wird aus dem Sessel springen! Aber sich danach auch besser fühlen. Hoffentlich. Erhöht Intelligenz um <%= int %>. Limitierte Ausgabe 20212022 Winterausrüstung.", "weaponSpecialWinter2022HealerNotes": "Berühre einen Freund mit diesem Werkzeug aus festgefrorenem Wasser am Nacken, und er wird aus dem Sessel springen! Aber sich danach auch besser fühlen. Hoffentlich. Erhöht Intelligenz um <%= int %>. Limitierte Ausgabe 20212022 Winterausrüstung.",
"armorSpecialWinter2022RogueText": "Strahlende Explosion", "armorSpecialWinter2022RogueText": "Strahlende Explosion",
"armorSpecialWinter2022WarriorText": "Samtiger Strumpf", "armorSpecialWinter2022WarriorText": "Flauschiger Strumpf",
"armorSpecialWinter2022MageText": "Granatapfel-Schutzrüstung", "armorSpecialWinter2022MageText": "Granatapfel-Schutzrüstung",
"armorSpecialWinter2022HealerText": "Kristallklare Rüstung aus Eis", "armorSpecialWinter2022HealerText": "Kristallklare Rüstung aus Eis",
"armorSpecialWinter2022HealerNotes": "Gleite wie auf Schlittschuhen knapp über dem Boden eine ätherisch-glitzernde Gestalt, gekommen, um Geduld und Gelassenheit zu verbreiten. Erhöht Ausdauer um <%= con %>. Limitierte Ausgabe 20212022 Winterausrüstung.", "armorSpecialWinter2022HealerNotes": "Gleite wie auf Schlittschuhen knapp über dem Boden eine ätherisch-glitzernde Gestalt, gekommen, um Geduld und Gelassenheit zu verbreiten. Erhöht Ausdauer um <%= con %>. Limitierte Ausgabe 20212022 Winterausrüstung.",
@@ -2494,7 +2494,7 @@
"headSpecialWinter2022MageText": "Granatapfelhelm", "headSpecialWinter2022MageText": "Granatapfelhelm",
"headSpecialWinter2022HealerText": "Kristallklare Krone aus Eis", "headSpecialWinter2022HealerText": "Kristallklare Krone aus Eis",
"weaponSpecialWinter2022RogueText": "Sternschnuppenfeuerwerk", "weaponSpecialWinter2022RogueText": "Sternschnuppenfeuerwerk",
"armorSpecialWinter2022RogueNotes": "Wenn sie Sterne sehen, sehen sie Dich nicht! Ja, lass uns das so sagen. Erhöht Wahrnehmung um <%= per %>. Limitierte Ausgabe 20212022 Winterausrüstung.", "armorSpecialWinter2022RogueNotes": "Wenn sie Sterne sehen, dann werden sie dich nicht sehen! Ja, lass es uns so machen. Erhöht Wahrnehmung um <%= per %>. Limitierte Ausgabe 20212022 Winterausrüstung.",
"armorSpecialWinter2022WarriorNotes": "Wer sagt, dass Du es nicht geborgen und gemütlich haben kannst, während Du mit alltäglichen Aufgaben kämpfst? Erhöht Ausdauer um <%= con %>. Limitierte Ausgabe 20212022 Winterausrüstung.", "armorSpecialWinter2022WarriorNotes": "Wer sagt, dass Du es nicht geborgen und gemütlich haben kannst, während Du mit alltäglichen Aufgaben kämpfst? Erhöht Ausdauer um <%= con %>. Limitierte Ausgabe 20212022 Winterausrüstung.",
"armorSpecialWinter2022MageNotes": "Wenn Du Dich näherst, müssen sich Deine Feinde sich vor Fruchtsaft-Flecken in Acht nehmen! Erhöht Intelligenz um <%= int %>. Limitierte Ausgabe 20212022 Winterausrüstung.", "armorSpecialWinter2022MageNotes": "Wenn Du Dich näherst, müssen sich Deine Feinde sich vor Fruchtsaft-Flecken in Acht nehmen! Erhöht Intelligenz um <%= int %>. Limitierte Ausgabe 20212022 Winterausrüstung.",
"headSpecialWinter2022RogueText": "Donnerndes Finale", "headSpecialWinter2022RogueText": "Donnerndes Finale",
@@ -2708,7 +2708,7 @@
"weaponSpecialWinter2023RogueText": "Grüne Satin Schärpe", "weaponSpecialWinter2023RogueText": "Grüne Satin Schärpe",
"weaponSpecialWinter2023WarriorText": "Stoßzahn Speer", "weaponSpecialWinter2023WarriorText": "Stoßzahn Speer",
"weaponSpecialSpring2023MageText": "Mondstein Magie", "weaponSpecialSpring2023MageText": "Mondstein Magie",
"weaponSpecialSpring2023MageNotes": "Je stärker ihr Glühen, desto größer ist ihre Macht. Erhöht Intelligenz um <%= int %>. Limitierte Ausgabe 2023 Frühlingsausrüstung.", "weaponSpecialSpring2023MageNotes": "Je stärker ihr Glühen, desto größer ist ihre Macht. Erhöht Intelligenz um <%= int %> und Wahrnehmung um <%= per %>. Limitierte Ausgabe 2023 Frühlingsausrüstung.",
"weaponSpecialSpring2023HealerText": "Lilien Pollen", "weaponSpecialSpring2023HealerText": "Lilien Pollen",
"weaponSpecialSummer2023WarriorText": "Wasserelementar-Schwert", "weaponSpecialSummer2023WarriorText": "Wasserelementar-Schwert",
"weaponSpecialSummer2023MageText": "Fisch", "weaponSpecialSummer2023MageText": "Fisch",
@@ -2945,8 +2945,8 @@
"armorArmoireHattersSuitNotes": "Dein Outfit ist nicht vollständig ohne deine grüne Glücks-Fliege. Trage dies zu deinem nächsten verrückten Teekränzchen. Oder angenehmen Teekränzchen. Oder begeisterten Teekränzchen. Oder... Erhöht Ausdauer um <%= con %>. Verzauberter Schrank: Hutmacher-Set (Gegenstand 2 von 4).", "armorArmoireHattersSuitNotes": "Dein Outfit ist nicht vollständig ohne deine grüne Glücks-Fliege. Trage dies zu deinem nächsten verrückten Teekränzchen. Oder angenehmen Teekränzchen. Oder begeisterten Teekränzchen. Oder... Erhöht Ausdauer um <%= con %>. Verzauberter Schrank: Hutmacher-Set (Gegenstand 2 von 4).",
"armorArmoireBlueStripedSwimsuitNotes": "Was könnte spannender sein als Seemonster am Strand zu bekämpfen? Erhöht Ausdauer um <%= con %>. Verzauberter Schrank: Strand-Set (Gegenstand 2 von 4).", "armorArmoireBlueStripedSwimsuitNotes": "Was könnte spannender sein als Seemonster am Strand zu bekämpfen? Erhöht Ausdauer um <%= con %>. Verzauberter Schrank: Strand-Set (Gegenstand 2 von 4).",
"armorArmoireSchoolUniformPantsNotes": "Ob du nun eine Schule für magische Zauberer, Drachenreiter, Ballsportspieler, kreative Künstler oder Mitglieder einer Organisation, die zu geheim ist, um sie hier aufzuzählen, besuchst, du wirst mit dieser Uniform gut dazu passen. Erhöht Intelligenz um <%= int %>.Verzauberter Schrank: Schuluniform-Set (Gegenstand 2 von 4).", "armorArmoireSchoolUniformPantsNotes": "Ob du nun eine Schule für magische Zauberer, Drachenreiter, Ballsportspieler, kreative Künstler oder Mitglieder einer Organisation, die zu geheim ist, um sie hier aufzuzählen, besuchst, du wirst mit dieser Uniform gut dazu passen. Erhöht Intelligenz um <%= int %>.Verzauberter Schrank: Schuluniform-Set (Gegenstand 2 von 4).",
"armorArmoireGreenFluffTrimmedCoatNotes": "Sagen erzählen, dass einmal in einer Generation ein Mantel daherkommt, der der wärmste und bequemste von allen ist. Sein Flausch ist außergewöhnlich und seine Knöpfe sind sogar von fäustlingsumhüllten Händen handhabbar. Das ist dieser Mantel hier. Erhöht Stärke und Intelligenz um jeweils <%= attrs %>. Verzauberter Schrank: Fallenstellerhut-Set (Gegenstand 2 von 2).", "armorArmoireGreenFluffTrimmedCoatNotes": "Sagen erzählen, dass einmal in einer Generation ein Mantel daherkommt, welcher der wärmste und bequemste von allen ist. Sein Flausch ist außergewöhnlich und seine Knöpfe sind sogar von fäustlingsumhüllten Händen handhabbar. Das ist dieser Mantel hier. Erhöht Stärke und Intelligenz um jeweils <%= attrs %>. Verzauberter Schrank: Fallenstellerhut-Set (Gegenstand 2 von 2).",
"armorArmoireSoftWhiteSuitNotes": "Weiß ist eine friedvolle Farbe. Ob du nun ein strahlend weißes Bettlaken oder eine Decke aus frischgefallenem Schnee betrachtest, du wirst einen klaren und vorbereiteten Geist haben. Erhöht Ausdauer um <% con %> und Wahrnehmung um <%= per %>. Verzauberter Schrank: Weiße Lounge-Kleidung (Gegenstand 2 von 3).", "armorArmoireSoftWhiteSuitNotes": "Weiß ist eine friedvolle Farbe. Ob du nun ein strahlend weißes Bettlaken oder eine Decke aus frischgefallenem Schnee betrachtest, du wirst einen klaren und vorbereiteten Geist haben. Erhöht Ausdauer um <%= con %> und Wahrnehmung um <%= per %>. Verzauberter Schrank: Weißes Lounge-Kleidungsset (Gegenstand 2 von 3).",
"armorArmoireCorsairsCoatAndCapeText": "Mantel und Umhang des Korsaren", "armorArmoireCorsairsCoatAndCapeText": "Mantel und Umhang des Korsaren",
"armorArmoireSmileyShirtNotes": "Weil du glücklich bist! Zeig der Welt, dass du heute den ganzen Tag lächelst. Erhöht Intelligenz und Wahrnehmung um jeweils <%= attrs %>. Verzauberter Schrank: Optimisten-Set (Gegenstand 1 von 4).", "armorArmoireSmileyShirtNotes": "Weil du glücklich bist! Zeig der Welt, dass du heute den ganzen Tag lächelst. Erhöht Intelligenz und Wahrnehmung um jeweils <%= attrs %>. Verzauberter Schrank: Optimisten-Set (Gegenstand 1 von 4).",
"armorArmoireYellowStripedSwimsuitText": "Gelb gestreifter Badeanzug", "armorArmoireYellowStripedSwimsuitText": "Gelb gestreifter Badeanzug",
@@ -2954,7 +2954,7 @@
"armorArmoireGreenFluffTrimmedCoatText": "Mantel mit grünem Flauschband", "armorArmoireGreenFluffTrimmedCoatText": "Mantel mit grünem Flauschband",
"armorArmoireSchoolUniformSkirtText": "Schuluniform mit Rock", "armorArmoireSchoolUniformSkirtText": "Schuluniform mit Rock",
"armorArmoireHattersSuitText": "Anzug des Hutmachers", "armorArmoireHattersSuitText": "Anzug des Hutmachers",
"armorArmoireKarateGiText": "Karate Gi", "armorArmoireKarateGiText": "Karategi",
"armorArmoireKarateGiNotes": "Diese leichte Karate-Uniform ist perfekt für Training oder Wettbewerbe. Erhöht Stärke um <%= str %>. Verzauberter Schrank: Karate-Set (Gegenstand 1 von 10).", "armorArmoireKarateGiNotes": "Diese leichte Karate-Uniform ist perfekt für Training oder Wettbewerbe. Erhöht Stärke um <%= str %>. Verzauberter Schrank: Karate-Set (Gegenstand 1 von 10).",
"weaponArmoireStormKnightAxeText": "Axt des Sturmritters", "weaponArmoireStormKnightAxeText": "Axt des Sturmritters",
"weaponArmoireStormKnightAxeNotes": "Sammle deine Wut und schlage wie ein Donnerschlag zu! Erhöht Stärke um <%= str %>. Verzauberter Schrank: Sturmritter-Set (Gegenstand 3 von 3)", "weaponArmoireStormKnightAxeNotes": "Sammle deine Wut und schlage wie ein Donnerschlag zu! Erhöht Stärke um <%= str %>. Verzauberter Schrank: Sturmritter-Set (Gegenstand 3 von 3)",
@@ -3084,7 +3084,7 @@
"headSpecialWinter2025HealerText": "Lichterketten-Wirrwarr", "headSpecialWinter2025HealerText": "Lichterketten-Wirrwarr",
"headMystery202402Notes": "Diese hübsche rosa Mähne ist das perfekte Accessoire für den Februar und darüber hinaus. Gewährt keinen Attributbonus. Februar 2024 Abonnentengegenstand.", "headMystery202402Notes": "Diese hübsche rosa Mähne ist das perfekte Accessoire für den Februar und darüber hinaus. Gewährt keinen Attributbonus. Februar 2024 Abonnentengegenstand.",
"headMystery202402Text": "Paradiesisches Rosa Haar", "headMystery202402Text": "Paradiesisches Rosa Haar",
"headMystery202301Notes": "Dein Gehör wird so scharf sein, dass du das Hereinbrechen des Morgens und das Glitzern des Taus hören wirst. Gewährt keinen Attributbonus. Jänner 2023 Abonnentengegenstand.", "headMystery202301Notes": "Dein Gehör wird so scharf sein, dass du das Hereinbrechen des Morgens und das Glitzern des Taus hören wirst. Gewährt keinen Attributbonus. Januar 2023 Abonnentengegenstand.",
"headMystery202304Text": "Tiptop Teekannen-Deckel", "headMystery202304Text": "Tiptop Teekannen-Deckel",
"headMystery202304Notes": "Dieser Helm gewährt dir Immuni-Tee-t. April 2023 Abonnentengegenstand.", "headMystery202304Notes": "Dieser Helm gewährt dir Immuni-Tee-t. April 2023 Abonnentengegenstand.",
"headMystery202310Text": "Narrenkappe", "headMystery202310Text": "Narrenkappe",
@@ -3295,7 +3295,7 @@
"shieldSpecialSpring2025RogueText": "Kristallspitzen-Flegel", "shieldSpecialSpring2025RogueText": "Kristallspitzen-Flegel",
"shieldSpecialSpring2025RogueNotes": "Du kannst den Kristall nutzen, um eine produktive Zukunft für dich weiszusagen. Nutze die Gelegenheit und spring vorwärts! Erhöht Stärke um <%= str %>. Limitierte Ausgabe 2025 Frühlingsausrüstung.", "shieldSpecialSpring2025RogueNotes": "Du kannst den Kristall nutzen, um eine produktive Zukunft für dich weiszusagen. Nutze die Gelegenheit und spring vorwärts! Erhöht Stärke um <%= str %>. Limitierte Ausgabe 2025 Frühlingsausrüstung.",
"shieldSpecialSpring2025HealerText": "Plumeria Schild", "shieldSpecialSpring2025HealerText": "Plumeria Schild",
"shieldSpecialSpring2025HealerNotes": "Du kannst dieses spezielle Blütenblatt verwenden, um Güte zu sammeln oder um negative Gedanken wegzuschnipsen. Nutze die Gelegenheit und spring vorwärts! Erhöht Ausdauer um <%= con %>. Limitierte Ausgabe 2025 Ausrüstung.", "shieldSpecialSpring2025HealerNotes": "Du kannst dieses spezielle Blütenblatt verwenden, um Güte zu sammeln oder um negative Gedanken wegzuschnipsen. Nutze die Gelegenheit und spring vorwärts! Erhöht Ausdauer um <%= con %>. Limitierte Ausgabe 2025 Frühlingsausrüstung.",
"shieldSpecialSpring2025WarriorText": "Sonnenstrahl Schild", "shieldSpecialSpring2025WarriorText": "Sonnenstrahl Schild",
"shieldSpecialSpring2025WarriorNotes": "Du kannst deine Gegner für den Moment blenden, wenn die Sonne diesen Schild genau richtig trifft. Nutze den Vorteil und spring vorwärts! Erhöht Ausdauer um <%= con %>. Limitierte Ausgabe 2025 Frühlingsausrüstung.", "shieldSpecialSpring2025WarriorNotes": "Du kannst deine Gegner für den Moment blenden, wenn die Sonne diesen Schild genau richtig trifft. Nutze den Vorteil und spring vorwärts! Erhöht Ausdauer um <%= con %>. Limitierte Ausgabe 2025 Frühlingsausrüstung.",
"shieldArmoireSpringPetalUchiwaNotes": "Dieser tragbare Fächer mit schönem Blütenmuster bewirkt eine leichte Brise nur für dich, wenn das Wetter wärmer wird. Erhöht Intelligenz und Wahrnehmung um jeweils <%= attrs %>. Verzauberter Schrank: Frühlingsblüten Set (Gegenstand 2 von 2).", "shieldArmoireSpringPetalUchiwaNotes": "Dieser tragbare Fächer mit schönem Blütenmuster bewirkt eine leichte Brise nur für dich, wenn das Wetter wärmer wird. Erhöht Intelligenz und Wahrnehmung um jeweils <%= attrs %>. Verzauberter Schrank: Frühlingsblüten Set (Gegenstand 2 von 2).",
@@ -3341,5 +3341,39 @@
"armorSpecialSummer2025MageText": "Zwerglippfisch Anzug", "armorSpecialSummer2025MageText": "Zwerglippfisch Anzug",
"armorSpecialSummer2025MageNotes": "Dieser Anzug hat nicht nur atemberaubende Farben, sondern ermöglicht dir auch, anmutig durch's Wasser zu gleiten. Schwimmen oder Tanzen - du hast die Wahl! Erhöht Intelligenz um <%= int %>. Limitierte Ausgabe Sommerausrüstung 2025.", "armorSpecialSummer2025MageNotes": "Dieser Anzug hat nicht nur atemberaubende Farben, sondern ermöglicht dir auch, anmutig durch's Wasser zu gleiten. Schwimmen oder Tanzen - du hast die Wahl! Erhöht Intelligenz um <%= int %>. Limitierte Ausgabe Sommerausrüstung 2025.",
"armorArmoireBeekeepersSuitText": "Imkeranzug", "armorArmoireBeekeepersSuitText": "Imkeranzug",
"armorArmoireBeekeepersSuitNotes": "Schütze dich, während du nach deinen fleißigen Hummeln schaust. Erhöht Ausdauer um <%= con %>. Verzauberter Schrank: Imker Set (Gegenstand 2 von 4)" "armorArmoireBeekeepersSuitNotes": "Schütze dich, während du nach deinen fleißigen Hummeln schaust. Erhöht Ausdauer um <%= con %>. Verzauberter Schrank: Imker Set (Gegenstand 2 von 4)",
"headSpecialSummer2025RogueNotes": "Deine Sehkraft wird sich verbessern sobald Du diese Maske aufsetzt. Erhöht Wahrnehmung um <%= per %>. Limitierte Ausgabe Sommer 2025 Ausrüstung.",
"weaponSpecialSummer2025MageText": "Zweigkoralle",
"weaponSpecialSummer2025MageNotes": "Verzweige dich mit deinen Talenten und Fähigkeiten, um eine Reihe von verschiedenen Aufgaben anzugreifen. Erhöht Intelligenz um <%= int %> und Wahrnehmung um <%= per %>. Limitierte Ausgabe Sommerausrüstung 2025",
"headSpecialSummer2025WarriorText": "Kammmuschelhelm",
"headSpecialSummer2025WarriorNotes": "Undurchdringlich und perfekt zugespitzt wird dich dieser Helm sogar vor Seesternen beschützen. Erhöht Stärke um <%= str %>. Limitierte Ausgabe Sommerausrüstung 2025.",
"headSpecialSummer2025RogueText": "Tintenfischmaske",
"headSpecialSummer2025HealerText": "Ruderschnecken Helm",
"headSpecialSummer2025HealerNotes": "Glückwunsch, jetzt bist du ein Seeschmetterling. Erhöht Intelligenz um <%= int %>. Limitierte Ausgabe Sommerausrüstung 2025.",
"headSpecialSummer2025MageText": "Feenlippfisch Kopfschmuck",
"headSpecialSummer2025MageNotes": "Alle werden davon hypnotisiert sein, wie sich deine Flossen mit der Strömung bewegen. Erhöht Wahrnehmung um <%= per %>. Limitierte Ausgabe Sommerausrüstung 2025.",
"headMystery202507Notes": "Umgedrehte Mützen sind immer noch cool, oder? Gewährt keinen Attributbonus. Juli 2025 Abonnentengegenstand.",
"headMystery202507Text": "Draufgängerische Skater Mütze",
"armorArmoireFlyFishingWadersText": "Fliegenfischerhose",
"armorArmoireFlyFishingWadersNotes": "Halte dich vollkommen warm und trocken, wenn du in einen Bach, Teich, See oder Fluß watest. Erhöht Stärke und Ausdauer um jeweils <%= attrs %>. Verzauberter Schrank: Fliegenfischer Set (Gegenstand 2 von 3)",
"headArmoireBeekeepersHatText": "Imkerhut und Schleier",
"headArmoireBeekeepersHatNotes": "Schützt dein Gesicht, während Du dich um Deine brummenden Kumpels kümmerst. Erhöht Wahrnehmung um <%= per %>. Verzauberter Schrank: Bienenzüchter-Set (Gegenstand 1 von 4)",
"shieldSpecialSummer2025WarriorText": "Jakobsmuschelschild",
"shieldSpecialSummer2025WarriorNotes": "Die Farben sind schön, aber der Kamm ist gefährlich. Feinde, nehmt euch in Acht! Erhöht die Konstitution um <%= con %>. Limitierte Edition Sommer 2025 Ausrüstung.",
"headArmoireFlyFishingHatText": "Fliegenfischerhut",
"headArmoireFlyFishingHatNotes": "Mit breiter Krempe und Platz für einen Ersatzköder. Du solltest nicht ohne diese Schönheit zum Fliegenfischen gehen. Erhöht Stärke und Wahrnehmung jeweils um <%= attrs %>. Verzauberter Schrank: Fliegenfischer-Set (Gegenstand 1 von 3)",
"shieldArmoireFlyFishingRodNotes": "Hänge einen Köder an diese lange, flexible Rute und Fische werden ihn jedes Mal mit einem Insekt verwechseln. Erhöht Stärke und Intelligenz um jeweils <%= attrs %>. Verzauberter Schrank: Fliegenfischer-Set (Gegenstand 3 von 3)",
"backMystery202506Text": "Sonnenschein-Aureole",
"shieldSpecialSummer2025RogueText": "Tintenfisch-Tentakel",
"shieldSpecialSummer2025RogueNotes": "Dieser Tentakel sieht biegsam aus, aber seine Saugnäpfe greifen fest zu. Feinde, passt auf! Erhöht die Stärke um <%= str %>. Limitierte Edition Sommer 2025 Ausrüstung.",
"shieldSpecialSummer2025HealerText": "Ruderschnecken-Schild",
"shieldSpecialSummer2025HealerNotes": "Dieser Schild sieht einfach aus, aber er hat gesundheitsschädliche Eigenschaften. Feinde, passt auf! Erhöht die Konstitution um <%= con %>. Limitierte Edition Sommer 2025 Ausrüstung.",
"shieldArmoireBeekeepersHiveText": "Bienenstock",
"shieldArmoireBeekeepersHiveNotes": "Bienenstöcke dienen sowohl als Zuhause als auch als Arbeitsplatz. Vielleicht solltet ihr mit euren Bienen über eine gute Work-Life-Balance sprechen. Erhöht die Stärke um <%= str %>. Verzauberter Schrank: Imker-Set (Gegenstand 4 von 4)",
"shieldArmoireFlyFishingRodText": "Fliegenfischer-Rute",
"backMystery202507Text": "Draufgängerisches Skateboard",
"backMystery202507Notes": "Dein Ross für Bürgersteige und Halfpipes. Gewährt keinen Attributbonus. Juli 2025 Abonnentengegenstand.",
"shieldMystery202506Text": "Sonnenschein-Schild",
"shieldMystery202506Notes": "Vertreibe die Dunkelheit und lasse deiner Umgebung warme und herzliche Strahlen zuteilwerden wo immer du auch bist. Gewährt keinen Attributbonus. Juni 2025 Abonnentengegenstand.",
"backMystery202506Notes": "Trage ein warmes Leuchten mit dir, wenn du deinen täglichen Aufgaben nachgehst. Gewährt keinen Attributbonus. Juni 2025 Abonnentengegenstand."
} }

View File

@@ -189,7 +189,7 @@
"dismissAll": "Alle entfernen", "dismissAll": "Alle entfernen",
"messages": "Nachrichten", "messages": "Nachrichten",
"emptyMessagesLine1": "Du hast im Moment keine Nachrichten", "emptyMessagesLine1": "Du hast im Moment keine Nachrichten",
"emptyMessagesLine2": "Du kannst anderen eine neue Nachricht schicken, indem Du ihr Profil aufrufst und auf den \"Nachrichten\"-Knopf drückst.", "emptyMessagesLine2": "Sende eine Nachricht, um eine Konversation mit Mitgliedern deiner Gruppe oder anderen Habitica Spielern zu beginnen",
"userSentMessage": "<span class=\"notification-bold\"><%- user %></span> hat Dir eine Nachricht gesendet", "userSentMessage": "<span class=\"notification-bold\"><%- user %></span> hat Dir eine Nachricht gesendet",
"letsgo": "Auf geht's!", "letsgo": "Auf geht's!",
"selected": "Ausgewählt", "selected": "Ausgewählt",

View File

@@ -79,7 +79,7 @@
"newBaileyUpdate": "Neues Update von Bailey!", "newBaileyUpdate": "Neues Update von Bailey!",
"tellMeLater": "Erzähl es mir später", "tellMeLater": "Erzähl es mir später",
"dismissAlert": "Als gelesen markieren", "dismissAlert": "Als gelesen markieren",
"donateText3": "Als Open-Source-Projekt ist Habitica auf die Hilfe unserer Benutzer angewiesen. Das Geld, was Du für Edelsteine ausgibst, hilft uns dabei unsere Server am Laufen zu halten, ein paar Mitarbeiter zu bezahlen, neue Features zu entwickeln und unseren ehrenamtlichen Helferlein Anreize zu bieten", "donateText3": "Als Open-Source-Projekt ist Habitica auf die Hilfe unserer Benutzer angewiesen. Das Geld, das Du für Edelsteine ausgibst, hilft uns dabei unsere Server am Laufen zu halten, ein paar Mitarbeiter zu bezahlen, neue Features zu entwickeln und unseren ehrenamtlichen Helferlein Anreize zu bieten",
"card": "Kreditkarte", "card": "Kreditkarte",
"paymentMethods": "Kauf mit", "paymentMethods": "Kauf mit",
"paymentSuccessful": "Die Zahlung war erfolgreich!", "paymentSuccessful": "Die Zahlung war erfolgreich!",
@@ -125,7 +125,7 @@
"limitedAvailabilityMinutes": "Für <%= minutes %>min <%= seconds %>sek verfügbar", "limitedAvailabilityMinutes": "Für <%= minutes %>min <%= seconds %>sek verfügbar",
"limitedAvailabilityHours": "Für t <%= hours %>std und <%= minutes %>min verfügbar", "limitedAvailabilityHours": "Für t <%= hours %>std und <%= minutes %>min verfügbar",
"limitedAvailabilityDays": "Für <%= days %>t <%= hours %>std und <%= minutes %>min verfügbar", "limitedAvailabilityDays": "Für <%= days %>t <%= hours %>std und <%= minutes %>min verfügbar",
"amountExp": "<%= amount %> Exp", "amountExp": "<%= amount %> XP",
"helpSupportHabitica": "Hilf dabei, Habitica zu unterstützen", "helpSupportHabitica": "Hilf dabei, Habitica zu unterstützen",
"groupsPaymentSubBilling": "Dein nächstes Rechnungsdatum ist <strong><%= renewalDate %></strong>.", "groupsPaymentSubBilling": "Dein nächstes Rechnungsdatum ist <strong><%= renewalDate %></strong>.",
"groupsPaymentAutoRenew": "Dieses Abonnement läuft automatisch weiter, bis es gekündigt wird. Du kannst es im Gruppen-Abrechnungs-Tab kündigen.", "groupsPaymentAutoRenew": "Dieses Abonnement läuft automatisch weiter, bis es gekündigt wird. Du kannst es im Gruppen-Abrechnungs-Tab kündigen.",

View File

@@ -161,7 +161,7 @@
"questOwlDropOwlEgg": "Eule (Ei)", "questOwlDropOwlEgg": "Eule (Ei)",
"questOwlUnlockText": "Schaltet den Kauf von Euleneiern auf dem Marktplatz frei", "questOwlUnlockText": "Schaltet den Kauf von Euleneiern auf dem Marktplatz frei",
"questPenguinText": "Der Federvieh-Frost", "questPenguinText": "Der Federvieh-Frost",
"questPenguinNotes": "Obwohl es auf der Südspitze von Habitica ein heißer Sommertag ist, hat eine unnatürliche Kälte den Lively Lake befallen. Man hört das Heulen von starken, eisigen Winden und das Ufer fängt an zuzufrieren. Eisspitzen brechen aus dem Boden und verdrängen Gras und Dreck. @Melynnrose und @Breadstrings rennen zu Dir hinüber.<br><br>\"Hilfe!\" sagt @Melynnrose. \"Wir haben einen riesigen Pinguin hergebracht, um den See zuzufrieren damit wir alle schlittschuhlaufen können, aber uns sind die Fische ausgegangen, mit denen wir ihn gefüttert haben!\"<br><br>\"Er wurde wütend und friert mit seinem Eis-Atem alles zu, was er sieht!\" sagt @Breadstrings. \"Bitte, Du musst ihn überwältigen bevor wir alle von Eis bedeckt sind!\" Sieht aus, als ob Du das Gemüt dieses Pinguins ... etwas <em>abkühlen</em> musst.", "questPenguinNotes": "Obwohl es auf der Südspitze von Habitica ein heißer Sommertag ist, hat eine unnatürliche Kälte den Lively Lake befallen. Man hört das Heulen von starken, eisigen Winden und das Ufer fängt an zuzufrieren. Eisspitzen brechen aus dem Boden und verdrängen Gras und Dreck. @Melynnrose und @Breadstrings rennen zu Dir hinüber.<br><br>\"Hilfe!\" sagt @Melynnrose. \"Wir haben einen riesigen Pinguin hergebracht, um den See zuzufrieren damit wir alle schlittschuhlaufen können, aber uns sind die Fische ausgegangen, mit denen wir ihn gefüttert haben!\"<br><br>\"Er wurde wütend und friert mit seinem Eis-Atem alles zu, was er sieht!\" sagt @Breadstrings. \"Bitte, Du musst ihn überwältigen bevor wir alle von Eis bedeckt sind!\" Sieht aus, als müsstest Du das Gemüt dieses Pinguins ... etwas <em>abkühlen.</em>",
"questPenguinCompletion": "Mit der Niederlage des Pinguins beginnt das Eis zu schmelzen. Der riesige Pinguin setzt sich im Sonnenschein auf den Boden und schlürft einen Eimer Fische herunter. Er gleitet über den See und lässt dabei mit einem leichten Pusten nach unten glattes, glitzerndes Eis entstehen. Was für ein komischer Vogel! \"Es scheint so, als hätte er einige Eier hinterlassen,\" sagt @Painter de Cluster. <br><br>@Rattify lacht. \"Vielleicht werden diese Pinguine ein bisschen ... gechillter sein?\"", "questPenguinCompletion": "Mit der Niederlage des Pinguins beginnt das Eis zu schmelzen. Der riesige Pinguin setzt sich im Sonnenschein auf den Boden und schlürft einen Eimer Fische herunter. Er gleitet über den See und lässt dabei mit einem leichten Pusten nach unten glattes, glitzerndes Eis entstehen. Was für ein komischer Vogel! \"Es scheint so, als hätte er einige Eier hinterlassen,\" sagt @Painter de Cluster. <br><br>@Rattify lacht. \"Vielleicht werden diese Pinguine ein bisschen ... gechillter sein?\"",
"questPenguinBoss": "Frostpinguin", "questPenguinBoss": "Frostpinguin",
"questPenguinDropPenguinEgg": "Pinguin (Ei)", "questPenguinDropPenguinEgg": "Pinguin (Ei)",
@@ -289,7 +289,7 @@
"questSnakeDropSnakeEgg": "Schlange (Ei)", "questSnakeDropSnakeEgg": "Schlange (Ei)",
"questSnakeUnlockText": "Schaltet den Kauf von Schlangeneiern auf dem Marktplatz frei", "questSnakeUnlockText": "Schaltet den Kauf von Schlangeneiern auf dem Marktplatz frei",
"questUnicornText": "Überzeuge die Einhornkönigin", "questUnicornText": "Überzeuge die Einhornkönigin",
"questUnicornNotes": "Conquest Creek ist völlig verschlammt und zerstört so Habit Citys Frischwasser-Vorräte! Glücklicherweise kennt @Lukreja eine alte Legende, laut welcher das Horn eines Einhorns in der Lage ist selbst das fauleste aller Wasser zu reinigen. Gemeinsam mit Deinem unerschrockenen Führer @UncommonCriminal machst Du Dich auf die Reise durch die, im ewigen Eis verborgenen, Gipfel der Mäandergebirge.\nLetztendlich, auf der eisigen Spitze von Mount Habitica erblickst Du inmitten des funkelnden Schneetreibens die Einhornkönigin höchstselbst.\n\"Dein Gesuch ist überzeugend\", spricht sie. \"Aber zuerst musst Du Dich meiner Hilfe als würdig erweisen!\"", "questUnicornNotes": "Conquest Creek ist völlig verschlammt und zerstört so Habit Citys Frischwasser-Vorräte! Glücklicherweise kennt @Lukreja eine alte Legende, laut welcher das Horn eines Einhorns in der Lage ist selbst das dreckigste Wasser zu reinigen. Gemeinsam mit Deinem unerschrockenen Führer @UncommonCriminal machst Du Dich auf die Reise durch die, im ewigen Eis verborgenen, Gipfel der Mäandergebirge. Schließlich erblickst Du auf der eisigen Spitze von Mount Habitica inmitten des funkelnden Schneetreibens die Einhornkönigin höchstselbst. \"Dein Gesuch ist überzeugend\", spricht sie. \"Aber zuerst musst Du Dich meiner Hilfe als würdig erweisen!\"",
"questUnicornCompletion": "Von Deiner Aufrichtigkeit und Stärke beeindruckt stimmt die Einhornkönigin schließlich Deinem Anliegen zu. Auf Ihrem Rücken reitend fliegt ihr zum Ursprung des Conquest Creek. In dem Moment, als das güldene Horn der Königin das faulige Wasser berührt beginnt ein grell-blaues Licht aus dem Wasser zu steigen. Es blendet so sehr, dass Du nicht anders kannst als Deine Augen zu schließen. Als Du sie einen Augenblick später wieder öffnest ist die Königin verschwunden. Ehe Du darüber nachdenken kannst hörst Du @rosiesullys Freudenschrei: Das Wasser ist nun klar und am Ufer des Flusses erblickst Du drei glänzende Eier.", "questUnicornCompletion": "Von Deiner Aufrichtigkeit und Stärke beeindruckt stimmt die Einhornkönigin schließlich Deinem Anliegen zu. Auf Ihrem Rücken reitend fliegt ihr zum Ursprung des Conquest Creek. In dem Moment, als das güldene Horn der Königin das faulige Wasser berührt beginnt ein grell-blaues Licht aus dem Wasser zu steigen. Es blendet so sehr, dass Du nicht anders kannst als Deine Augen zu schließen. Als Du sie einen Augenblick später wieder öffnest ist die Königin verschwunden. Ehe Du darüber nachdenken kannst hörst Du @rosiesullys Freudenschrei: Das Wasser ist nun klar und am Ufer des Flusses erblickst Du drei glänzende Eier.",
"questUnicornBoss": "Die Einhornkönigin", "questUnicornBoss": "Die Einhornkönigin",
"questUnicornDropUnicornEgg": "Einhorn (Ei)", "questUnicornDropUnicornEgg": "Einhorn (Ei)",
@@ -314,8 +314,8 @@
"questSnailUnlockText": "Schaltet den Kauf von Schneckeneiern auf dem Marktplatz frei", "questSnailUnlockText": "Schaltet den Kauf von Schneckeneiern auf dem Marktplatz frei",
"questBewilderText": "Der Verwirrer", "questBewilderText": "Der Verwirrer",
"questBewilderNotes": "Die Party beginnt wie jede Andere.<br><br>Die Appetithäppchen sind exzellent, die Musik sorgt für lockere Stimmung und sogar an die tanzenden Elefanten hat man sich gewöhnt. Habiticaner lachen ausgelassen zwischen den ausladenden Blumengestecken, froh um eine Ablenkung von Ihren meist-gehassten Aufgaben, und der April-Scherzkeks wirbelt zwischen ihnen herum, eifrig Scherze und Schabernack treibend.<br><br>Als die Uhr des Mistiflying Uhrturms Mitternacht schlägt, springt der April-Scherzkeks auf die Bühne um eine Rede zu halten.<br><br>\"Freunde! Feinde! Tolerante Bekannte! Leiht mir euer Ohr!\" Als aus ihren Köpfen Tierohren sprießen, kichert die Schar und posiert mit ihren neuen Verkleidungen.<br><br>\"Wie ihr alle wisst,\" fährt der April-Scherzkeks weiter, \"dauern meine verwirrenden Illusionen normalerweise nur einen Tag. Deshalb freue ich mich besonders euch mitzuteilen, dass ich eine Abkürzung gefunden habe, die uns Spaß ohne Ende verspricht, ohne dass wir uns um die mühsame Last unserer Pflichten sorgen müssen. Liebe Habiticaner, ich stelle euch hiermit meinen neuen, magischen Freund vor: den Verwirrer!\"<br><br>Lemoness wird plötzlich bleich und lässt ihre Häppchen fallen. \"Wartet! Traut ihm ni--\"<br><br>Doch schon stürzen Nebelschwaden in den Saal, dick und glitzernd. Sie wirbeln um den April-Scherzkeks und vereinen sich zu verschwommenen Federn und einem langen Hals. The Menge ist sprachlos, während sich vor ihr ein monströser Vogel entfaltet, seine Flügel schimmernd vor lauter Illusionen. Er gibt ein fürchterliches, kreischendes Lachen von sich.<br><br>\"Oh, es ist schon Ewigkeiten her, seit ein Habiticaner töricht genug war, mich herbeizurufen! Wie wundervoll es ist, endlich eine greifbare Form zu haben!\"<br><br>Erschreckt summend fliehen die magischen Bienen von Mistiflying aus der fliegenden Stadt, die aus dem Himmel sackt. Eine nach der anderen verwelken die leuchtenden Frühlingsblumen und schrumpfen weg.<br><br>\"Meine liebsten Freunde, warum so beunruhigt?\" krächzt der Verwirrer, während er mit den Flügeln schlägt. \"Es gibt keinen Grund mehr, euch für eure Belohnungen abzurackern. Ich werde euch alles geben, was ihr euch wünscht!\"<br><br>Ein Münzenschauer stürzt aus dem Himmel und hämmert sich mit brutaler Kraft in den Boden. Die Menge schreit und sucht Deckung. \"Soll das ein Scherz sein?\" ruft Baconsaur, während das Gold Fenster einschlägt und die Schindeln auf den Dächern zerschmettert.<br><br>PainterProphet duckt sich, als Blitze über den Himmel schießen und Nebel die Sonne verdunkelt. \"Nein! Diesmal ist es glaube ich kein Scherz!\"<br><br>Schnell, Habiticaner, lasst diesen Weltboss euch nicht von euren Zielen ablenken! Konzentriert euch auf das, was ihr zu erledigen habt, um Mistiflying zu retten -- und hoffentlich euch selbst.", "questBewilderNotes": "Die Party beginnt wie jede Andere.<br><br>Die Appetithäppchen sind exzellent, die Musik sorgt für lockere Stimmung und sogar an die tanzenden Elefanten hat man sich gewöhnt. Habiticaner lachen ausgelassen zwischen den ausladenden Blumengestecken, froh um eine Ablenkung von Ihren meist-gehassten Aufgaben, und der April-Scherzkeks wirbelt zwischen ihnen herum, eifrig Scherze und Schabernack treibend.<br><br>Als die Uhr des Mistiflying Uhrturms Mitternacht schlägt, springt der April-Scherzkeks auf die Bühne um eine Rede zu halten.<br><br>\"Freunde! Feinde! Tolerante Bekannte! Leiht mir euer Ohr!\" Als aus ihren Köpfen Tierohren sprießen, kichert die Schar und posiert mit ihren neuen Verkleidungen.<br><br>\"Wie ihr alle wisst,\" fährt der April-Scherzkeks weiter, \"dauern meine verwirrenden Illusionen normalerweise nur einen Tag. Deshalb freue ich mich besonders euch mitzuteilen, dass ich eine Abkürzung gefunden habe, die uns Spaß ohne Ende verspricht, ohne dass wir uns um die mühsame Last unserer Pflichten sorgen müssen. Liebe Habiticaner, ich stelle euch hiermit meinen neuen, magischen Freund vor: den Verwirrer!\"<br><br>Lemoness wird plötzlich bleich und lässt ihre Häppchen fallen. \"Wartet! Traut ihm ni--\"<br><br>Doch schon stürzen Nebelschwaden in den Saal, dick und glitzernd. Sie wirbeln um den April-Scherzkeks und vereinen sich zu verschwommenen Federn und einem langen Hals. The Menge ist sprachlos, während sich vor ihr ein monströser Vogel entfaltet, seine Flügel schimmernd vor lauter Illusionen. Er gibt ein fürchterliches, kreischendes Lachen von sich.<br><br>\"Oh, es ist schon Ewigkeiten her, seit ein Habiticaner töricht genug war, mich herbeizurufen! Wie wundervoll es ist, endlich eine greifbare Form zu haben!\"<br><br>Erschreckt summend fliehen die magischen Bienen von Mistiflying aus der fliegenden Stadt, die aus dem Himmel sackt. Eine nach der anderen verwelken die leuchtenden Frühlingsblumen und schrumpfen weg.<br><br>\"Meine liebsten Freunde, warum so beunruhigt?\" krächzt der Verwirrer, während er mit den Flügeln schlägt. \"Es gibt keinen Grund mehr, euch für eure Belohnungen abzurackern. Ich werde euch alles geben, was ihr euch wünscht!\"<br><br>Ein Münzenschauer stürzt aus dem Himmel und hämmert sich mit brutaler Kraft in den Boden. Die Menge schreit und sucht Deckung. \"Soll das ein Scherz sein?\" ruft Baconsaur, während das Gold Fenster einschlägt und die Schindeln auf den Dächern zerschmettert.<br><br>PainterProphet duckt sich, als Blitze über den Himmel schießen und Nebel die Sonne verdunkelt. \"Nein! Diesmal ist es glaube ich kein Scherz!\"<br><br>Schnell, Habiticaner, lasst diesen Weltboss euch nicht von euren Zielen ablenken! Konzentriert euch auf das, was ihr zu erledigen habt, um Mistiflying zu retten -- und hoffentlich euch selbst.",
"questBewilderCompletion": "<strong>Der Verwirrer ist BESIEGT!</strong><br><br>Wir haben es geschafft! Der Verwirrer stößt einen heulenden Schrei aus, windet sich in der Luft und verliert büschelweise Federn. Langsam, nach und nach wickelt er sich zu einer funkelnden Nebelwoke auf. Die enthüllte Sonne durchdingt den Nebel, vertreibt ihn und enthüllt die hustenden, glücklicherweise menschlichen Formen von Matt, Bailey, Alex ... und dem April-Scherzkeks persönlich.<br><br><strong>Mistiflying ist gerettet!</strong><br><br>Der April-Scherzkeks schämt sich immerhin genug, um etwas verlegen dazustehen. \"Oh, ähm,\" sagt er. \"Vielleicht habe ich mich ein wenig ... gehen lassen.\"”<br><br>Die Menschenmenge murrt. Durchnässte Blumen werden auf Gehsteigen angeschwemmt. Irgendwo in der Ferne stürzt ein Dach mit spektakulärem Krachen ein.<br><br>\"Ähm, ja,\" sagt der April-Scherzkeks. \"Also, was ich sagen wollte, es tut mir schrecklich leid.\" Er seufzt schwer. \"Ich vermute mal, es kann doch nicht nur Spaß und Spiel geben. Es kann wohl nicht schaden, sich ab und zu mal auf etwas zu konzentrieren. Vielleicht kann ich ja schon mal den nächsten Aprilscherz vorbereiten.\"<br><br>Redphoenix räuspert sich vielsagend.<br><br>\"Ich meine, den nächsten Frühjahrsputz!\" korrigiert sich der April-Scherzkeks. \"Habt keine Angst, ich werde Habit City rasch wieder blitzblank haben. Niemand ist besser als ich mit dem Doppelmopp.\"<br><br>Ermuntert beginnt die Kapelle zu spielen.<br><br>Es dauert nicht lange bis alles in Habit City wieder seinen normalen Lauf nimmt. Außerdem, jetzt wo der Verwirrer pulverisiert ist, sind die magischen Bienen von Mistiflying wieder eifrig am Werk und schon bald blühen die Blumen und die Stadt schwebt von Neuem.<br><br>Wie die Habiticaner die magischen, flaumigen Bienen knuddeln, beginnen die Augen des April-Scherzkeks zu leuchten. \"Oho, mir kommt eine Idee! Warum haltet ihr euch eigentlich keine dieser flaumigen Bienen als Haustiere und Reittiere? Das ist ein Geschenk, das das Gleichgewicht zwischen harter Arbeit und süßer Belohnung symbolisiert, wenn ich das mal so langweilig und sinnbildlich sagen darf.\" Er zwinkert. \"Und außerdem haben sie keinen Stachel! Narrenehrenwort.\"", "questBewilderCompletion": "<strong>Der Verwirrer ist BESIEGT!</strong><br><br>Wir haben es geschafft! Der Verwirrer stößt einen heulenden Schrei aus, windet sich in der Luft und verliert büschelweise Federn. Langsam, nach und nach wickelt er sich zu einer funkelnden Nebelwoke auf. Die enthüllte Sonne durchdingt den Nebel, vertreibt ihn und enthüllt die hustenden, glücklicherweise menschlichen Formen von Matt, Bailey, Alex ... und dem April-Scherzkeks persönlich.<br><br><strong>Mistiflying ist gerettet!</strong><br><br>Der April-Scherzkeks schämt sich immerhin genug, um etwas verlegen dazustehen. \"Oh, ähm,\" sagt er. \"Vielleicht habe ich mich ein wenig ... gehen lassen.\"”<br><br>Die Menschenmenge murrt. Durchnässte Blumen werden auf Gehsteigen angeschwemmt. Irgendwo in der Ferne stürzt ein Dach mit spektakulärem Krachen ein.<br><br>\"Ähm, ja,\" sagt der April-Scherzkeks. \"Also, was ich sagen wollte, es tut mir schrecklich leid.\" Er seufzt schwer. \"Ich vermute mal, es kann doch nicht nur Spaß und Spiel geben. Es kann wohl nicht schaden, sich ab und zu mal auf etwas zu konzentrieren. Vielleicht kann ich ja schon mal den nächsten Aprilscherz vorbereiten.\"<br><br>Redphoenix räuspert sich vielsagend.<br><br>\"Ich meine, den nächsten Frühjahrsputz!\" korrigiert sich der April-Scherzkeks. \"Habt keine Angst, ich werde Habit City rasch wieder blitzblank haben. Niemand ist besser als ich mit dem Doppelmopp.\"<br><br>Ermuntert beginnt die Kapelle zu spielen.<br><br>Es dauert nicht lange bis alles in Habit City wieder seinen normalen Lauf nimmt. Außerdem, jetzt wo der Verwirrer pulverisiert ist, sind die magischen Bienen von Mistiflying wieder eifrig am Werk und schon bald blühen die Blumen und die Stadt schwebt von Neuem.<br><br>Als die Habiticaner die magischen, flaumigen Bienen knuddeln, beginnen die Augen des April-Scherzkeks zu leuchten. \"Oho, mir kommt eine Idee! Warum haltet ihr euch eigentlich keine dieser flaumigen Bienen als Haustiere und Reittiere? Das ist ein Geschenk, welches das Gleichgewicht zwischen harter Arbeit und süßer Belohnung symbolisiert, wenn ich das mal so langweilig und sinnbildlich sagen darf.\" Er zwinkert. \"Und außerdem haben sie keinen Stachel! Narrenehrenwort.\"",
"questBewilderCompletionChat": "`Der Verwirrer is BESIEGT!`\n\nWir haben es geschafft! Der Verwirrer stößt einen heulenden Schrei aus, windet sich in der Luft und verliert büschelweise Federn. Langsam, nach und nach wickelt er sich zu einer funkelnden Nebelwoke auf. Die enthüllte Sonne durchdingt den Nebel, vertreibt ihn und enthüllt die hustenden, glücklicherweise menschlichen Formen von Matt, Bailey, Alex ... und dem April-Scherzkeks persönlich.\n\n`Mistiflying ist gerettet!`\n\nDer April-Scherzkeks schämt sich immerhin genug, um etwas verlegen dazustehen. \"Oh, ähm,\" sagt er. \"Vielleicht habe ich mich ein wenig ... gehen lassen.\"\n\nDie Menschenmenge murrt. Durchnässte Blumen werden auf Gehsteigen angeschwemmt. Irgendwo in der Ferne stürzt ein Dach mit spektakulärem Krachen ein.\n\n\"Ähm, ja,\" sagt der April-Scherzkeks. \"Also, was ich sagen wollte, es tut mir schrecklich leid.\" Er seufzt schwer. \"Ich vermute mal, es kann doch nicht nur Spaß und Spiel geben. Es kann wohl nicht schaden, sich ab und zu mal auf etwas zu konzentrieren. Vielleicht kann ich ja schon mal den nächsten Aprilscherz vorbereiten.\"\n\nRedphoenix räuspert sich vielsagend.\n\n\"Ich meine, den nächsten Frühjahrsputz!\" korrigiert sich der April-Scherzkeks. \"Habt keine Angst, ich werde Habit City rasch wieder blitzblank haben. Niemand ist besser als ich mit dem Doppelmopp.\"\n\nErmuntert beginnt die Kapelle zu spielen\n\nEs dauert nicht lange bis alles in Habit City wieder seinen normalen Lauf nimmt. Außerdem, jetzt wo der Verwirrer pulverisiert ist, sind die magischen Bienen von Mistiflying wieder eifrig am Werk und schon bald blühen die Blumen und schwebt die Stadt von Neuem.\n\nWie die Habiticaner die magischen, flaumigen Bienen knuddeln, beginnen die Augen des April-Scherzkeks zu leuchten. \"Oho, mir kommt eine Idee! Warum haltet ihr euch eigentlich keine dieser flaumigen Bienen als Haustiere und Reittiere? Das ist ein Geschenk, das das Gleichgewicht zwischen harter Arbeit und süßer Belohnung symbolisiert, wenn ich das mal so langweilig und sinnbildlich sagen darf.\" Er zwinkert. \"Und außerdem haben sie keinen Stachel! Narrenehrenwort.\"", "questBewilderCompletionChat": "`Der Verwirrer is BESIEGT!`\n\nWir haben es geschafft! Der Verwirrer stößt einen heulenden Schrei aus, windet sich in der Luft und verliert büschelweise Federn. Langsam, nach und nach wickelt er sich zu einer funkelnden Nebelwoke auf. Die enthüllte Sonne durchdingt den Nebel, vertreibt ihn und enthüllt die hustenden, glücklicherweise menschlichen Formen von Matt, Bailey, Alex ... und dem April-Scherzkeks persönlich.\n\n`Mistiflying ist gerettet!`\n\nDer April-Scherzkeks schämt sich immerhin genug, um etwas verlegen dazustehen. \"Oh, ähm,\" sagt er. \"Vielleicht habe ich mich ein wenig ... gehen lassen.\"\n\nDie Menschenmenge murrt. Durchnässte Blumen werden auf Gehsteigen angeschwemmt. Irgendwo in der Ferne stürzt ein Dach mit spektakulärem Krachen ein.\n\n\"Ähm, ja,\" sagt der April-Scherzkeks. \"Also, was ich sagen wollte, es tut mir schrecklich leid.\" Er seufzt schwer. \"Ich vermute mal, es kann doch nicht nur Spaß und Spiel geben. Es kann wohl nicht schaden, sich ab und zu mal auf etwas zu konzentrieren. Vielleicht kann ich ja schon mal den nächsten Aprilscherz vorbereiten.\"\n\nRedphoenix räuspert sich vielsagend.\n\n\"Ich meine, den nächsten Frühjahrsputz!\" korrigiert sich der April-Scherzkeks. \"Habt keine Angst, ich werde Habit City rasch wieder blitzblank haben. Niemand ist besser als ich mit dem Doppelmopp.\"\n\nErmuntert beginnt die Kapelle zu spielen\n\nEs dauert nicht lange bis alles in Habit City wieder seinen normalen Lauf nimmt. Außerdem, jetzt wo der Verwirrer pulverisiert ist, sind die magischen Bienen von Mistiflying wieder eifrig am Werk und schon bald blühen die Blumen und schwebt die Stadt von Neuem.\n\nAls die Habiticaner die magischen, flaumigen Bienen knuddeln, beginnen die Augen des April-Scherzkeks zu leuchten. \"Oho, mir kommt eine Idee! Warum haltet ihr euch eigentlich keine dieser flaumigen Bienen als Haustiere und Reittiere? Das ist ein Geschenk, welches das Gleichgewicht zwischen harter Arbeit und süßer Belohnung symbolisiert, wenn ich das mal so langweilig und sinnbildlich sagen darf.\" Er zwinkert. \"Und außerdem haben sie keinen Stachel! Narrenehrenwort.\"",
"questBewilderBossRageTitle": "Betörungsschlag", "questBewilderBossRageTitle": "Betörungsschlag",
"questBewilderBossRageDescription": "Wenn dieser Balken voll ist, wird der Verwirrer seinen Wirrschlag über Habitica entfesseln!", "questBewilderBossRageDescription": "Wenn dieser Balken voll ist, wird der Verwirrer seinen Wirrschlag über Habitica entfesseln!",
"questBewilderDropBumblebeePet": "Magische Biene (Haustier)", "questBewilderDropBumblebeePet": "Magische Biene (Haustier)",
@@ -409,7 +409,7 @@
"questMoon1CollectShards": "Mondscherben", "questMoon1CollectShards": "Mondscherben",
"questMoon1DropHeadgear": "Mondkriegerhelm (Kopfbedeckung)", "questMoon1DropHeadgear": "Mondkriegerhelm (Kopfbedeckung)",
"questMoon2Text": "Mondkampf, Teil 2: Stoppt den Überschattenden Stress", "questMoon2Text": "Mondkampf, Teil 2: Stoppt den Überschattenden Stress",
"questMoon2Notes": "Nachdem Sie die Splitter untersucht hat, hat @Starsystemic die Seherin schlechte Neuigkeiten: \"Ein altes Monster nähert sich Habitica und überfällt deren Einwohner dabei mit schrecklichem Stress. Ich kann den Schatten aus den Herzen der Leute abziehen und hierherbringen. Dabei wird es seine physische Form annehmen. Ihr müsst es dann besiegen, bevor es sich befreien und erneut ausbreiten kann.\" Du nickst und Sie beginnt ihre Zauberformeln zu sprechen. Tanzende Schatten erfüllen den Raum und schmiegen sich dicht aneinander. Kalte Winde kommen auf und die Dunkelheit verdichtet sich. Der Düsterschatten-Stress erhebt sich vom Boden, grinst wie ein wahrgewordener Albtraum ... und greift an!", "questMoon2Notes": "Nachdem der Untersuchung der Splitter, hat @Starsystemic die Seherin schlechte Neuigkeiten: \"Ein altes Monster nähert sich Habitica und überfällt deren Einwohner dabei mit schrecklichem Stress. Ich kann den Schatten aus den Herzen der Leute abziehen und hierherbringen. Dabei wird es seine physische Form annehmen. Ihr müsst es dann besiegen, bevor es sich befreien und erneut ausbreiten kann.\" Du nickst und Sie beginnt ihre Zauberformeln zu sprechen. Tanzende Schatten erfüllen den Raum und schmiegen sich dicht aneinander. Kalte Winde kommen auf und die Dunkelheit verdichtet sich. Der Düsterschatten-Stress erhebt sich vom Boden, grinst wie ein wahrgewordener Albtraum ... und greift an!",
"questMoon2Completion": "Der Schatten zerbirst in einem dunklen Windstoß, und hinterlässt den Raum heller und Eure Herzen strahlender. Der Stress, der Habitica überdeckte hat nachgelassen, und Ihr könnt alle erleichtert durchatment. Und dennoch, als Ihr nach oben in den Himmel seht, könnt Ihr fühlen, dass es noch nicht vorüber ist: Das Monster weiß, dass jemand seinen Schatten zerstört hat. \"Wir werden in den kommenden Wochen sorgfältig Ausschau halten,\" sagt @Starsystemic, \"und ich werde Euch eine Questschriftrolle schicken, sobald es sich manifestiert.\"", "questMoon2Completion": "Der Schatten zerbirst in einem dunklen Windstoß, und hinterlässt den Raum heller und Eure Herzen strahlender. Der Stress, der Habitica überdeckte hat nachgelassen, und Ihr könnt alle erleichtert durchatment. Und dennoch, als Ihr nach oben in den Himmel seht, könnt Ihr fühlen, dass es noch nicht vorüber ist: Das Monster weiß, dass jemand seinen Schatten zerstört hat. \"Wir werden in den kommenden Wochen sorgfältig Ausschau halten,\" sagt @Starsystemic, \"und ich werde Euch eine Questschriftrolle schicken, sobald es sich manifestiert.\"",
"questMoon2Boss": "Überschattender Stress", "questMoon2Boss": "Überschattender Stress",
"questMoon2DropArmor": "Mondkriegerrüstung (Rüstung)", "questMoon2DropArmor": "Mondkriegerrüstung (Rüstung)",
@@ -473,7 +473,7 @@
"questButterflyUnlockText": "Schaltet den Kauf von Raupeneiern auf dem Marktplatz frei", "questButterflyUnlockText": "Schaltet den Kauf von Raupeneiern auf dem Marktplatz frei",
"questGroupMayhemMistiflying": "Chaos in Mistiflying", "questGroupMayhemMistiflying": "Chaos in Mistiflying",
"questMayhemMistiflying1Text": "Chaos in Mistiflying, Teil 1: In welchem Mistiflying unter einer schrecklichen Plage leidet", "questMayhemMistiflying1Text": "Chaos in Mistiflying, Teil 1: In welchem Mistiflying unter einer schrecklichen Plage leidet",
"questMayhemMistiflying1Notes": "Obwohl die hiesigen Wahrsager angenehmes Wetter vorhersagten, ist der Wind am Nachmittag extrem böig, so dass Du Deiner Freundin @Kiwibot nur zu gerne in ihr Haus folgst, um dem stürmischen Treiben zu entkommen.<br><br>Keiner von euch hat den April-Scherzkeks erwartet, der am Küchentisch lehnt.<br><br>“Oh, hallo”, sagt er. “Wie schön, euch zu sehen. Darf ich euch etwas von diesem köstlichen Tee anbieten?”<br><br>“Das...” setzt @Kiwibot an. “Das ist MEIN—”<br><br>“Ja, ja, selbstverständlich”, sagt der April-Scherzkeks, und nimmt sich einige Kekse. “Ich dachte nur, ich spring' mal kurz rein, für eine kleine Verschnaufpause vor all den Tornado-beschwörenden Schädeln.” Er nimmt einen beiläufigen Schluck aus seiner Teetasse. “Mistiflying wird übrigens angegriffen.”<br><br>Entsetzt rennst Du mit Deinen Freunden zu den Ställen, um eure schnellsten geflügelten Reittiere zu satteln. Als ihr zu der schwebenden Stadt aufsteigt, könnt ihr einen Schwarm klappernder, fliegender Schädel ausmachen, die die Stadt belagern... und einige haben euch entdeckt!", "questMayhemMistiflying1Notes": "Obwohl die hiesigen Wahrsager angenehmes Wetter vorhersagten, ist der Wind am Nachmittag extrem böig, so dass Du Deiner Freundin @Kiwibot nur zu gerne in ihr Haus folgst, um dem stürmischen Treiben zu entkommen.<br><br>Keiner von euch hat den April-Scherzkeks erwartet, der am Küchentisch lehnt.<br><br>“Oh, hallo”, sagt er. “Wie schön, euch zu sehen. Darf ich euch etwas von diesem köstlichen Tee anbieten?”<br><br>“Das...” setzt @Kiwibot an. “Das ist MEIN—”<br><br>“Ja, ja, selbstverständlich”, sagt der April-Scherzkeks, und nimmt sich einige Kekse. “Ich dachte nur, ich spring' mal kurz rein, für eine kleine Verschnaufpause vor all den Tornado-beschwörenden Schädeln.” Er nimmt einen beiläufigen Schluck aus seiner Teetasse. “Mistiflying wird übrigens angegriffen.”<br><br>Entsetzt rennst Du mit Deinen Freunden zu den Ställen, um eure schnellsten geflügelten Reittiere zu satteln. Als ihr zu der schwebenden Stadt aufsteigt, könnt ihr einen Schwarm klappernder, fliegender Schädel ausmachen, welche die Stadt belagern... und einige haben euch entdeckt!",
"questMayhemMistiflying1Completion": "Der letzte Schädel fällt vom Himmel, ein schimmerndes Bündel aus Regenbogenroben zwischen seinen Kiefern, aber der stetige Wind hat nicht nachgelassen. Hier scheint noch etwas anderes vorzugehen. Und wo ist eigentlich dieser Drückeberger von April-Scherzkeks? Du hebst die Roben auf und fliegst in die Stadt.", "questMayhemMistiflying1Completion": "Der letzte Schädel fällt vom Himmel, ein schimmerndes Bündel aus Regenbogenroben zwischen seinen Kiefern, aber der stetige Wind hat nicht nachgelassen. Hier scheint noch etwas anderes vorzugehen. Und wo ist eigentlich dieser Drückeberger von April-Scherzkeks? Du hebst die Roben auf und fliegst in die Stadt.",
"questMayhemMistiflying1Boss": "Luftschädelschwarm", "questMayhemMistiflying1Boss": "Luftschädelschwarm",
"questMayhemMistiflying1RageTitle": "Schwarmnachwuchs", "questMayhemMistiflying1RageTitle": "Schwarmnachwuchs",
@@ -578,7 +578,7 @@
"questDysheartenerCompletionChat": "`Der Entmutiger wurde BESIEGT!'`\n\nGemeinsam holen alle in Habitica zu einem letzten Schlag mit ihren Aufgaben aus, und der Entmutiger weicht mit einem bestürzten Kreischen zurück. “Stimmt etwas nicht, Entmutiger?” ruft AnnDeLune mit funkelnden Augen. “Fühlst Du Dich entmutigt?”\n\nGlühend pinke Risse zeigen sich auf dem Panzer des Entmutigers, und er zerbricht mit einer verpuffenden rosa Rauchwolke. Eine Flut von köstlichen Süßigkeiten regnet auf alle hernieder, während sich im ganzen Land ein erneuertes Gefühl von Kraft und Entschlossenheit ausbreitet.\n\nDie Menge jubelt wild, Umarmungen werden ausgetauscht, und die Haustiere kauen glücklich auf ihren verspäteten Valentinsleckerli. Plötzlich liegt Gesang in der Luft, ein fröhlicher Chor ertönt, und funkelnde Silhouetten ziehen über den Himmel.\n\nUnser neu-gewonnener Optimismus hat eine Herde Hippogreifen angelockt! Die anmutigen Kreaturen setzen auf dem Boden auf, sträuben interessiert ihre Federn und tänzeln auf der Stelle. “Wie es aussieht, haben wir neue Freunde gefunden, die uns helfen, nicht zu verzagen, selbst wenn unsere Aufgaben noch so beängstigend sind”, sagt Lemoness.\n\nBeffymaroo hat bereits ihre Arme voll mit gefiederten Plüschbällen. “Vielleicht helfen sie uns, die zerstörten Gebiete Habitica's wieder aufzubauen!”\n\nSummend und singend ziehen die Hippogreifen voran, während alle Habiticaner zusammenarbeiten, um unsere geliebte Heimat wieder aufzubauen.", "questDysheartenerCompletionChat": "`Der Entmutiger wurde BESIEGT!'`\n\nGemeinsam holen alle in Habitica zu einem letzten Schlag mit ihren Aufgaben aus, und der Entmutiger weicht mit einem bestürzten Kreischen zurück. “Stimmt etwas nicht, Entmutiger?” ruft AnnDeLune mit funkelnden Augen. “Fühlst Du Dich entmutigt?”\n\nGlühend pinke Risse zeigen sich auf dem Panzer des Entmutigers, und er zerbricht mit einer verpuffenden rosa Rauchwolke. Eine Flut von köstlichen Süßigkeiten regnet auf alle hernieder, während sich im ganzen Land ein erneuertes Gefühl von Kraft und Entschlossenheit ausbreitet.\n\nDie Menge jubelt wild, Umarmungen werden ausgetauscht, und die Haustiere kauen glücklich auf ihren verspäteten Valentinsleckerli. Plötzlich liegt Gesang in der Luft, ein fröhlicher Chor ertönt, und funkelnde Silhouetten ziehen über den Himmel.\n\nUnser neu-gewonnener Optimismus hat eine Herde Hippogreifen angelockt! Die anmutigen Kreaturen setzen auf dem Boden auf, sträuben interessiert ihre Federn und tänzeln auf der Stelle. “Wie es aussieht, haben wir neue Freunde gefunden, die uns helfen, nicht zu verzagen, selbst wenn unsere Aufgaben noch so beängstigend sind”, sagt Lemoness.\n\nBeffymaroo hat bereits ihre Arme voll mit gefiederten Plüschbällen. “Vielleicht helfen sie uns, die zerstörten Gebiete Habitica's wieder aufzubauen!”\n\nSummend und singend ziehen die Hippogreifen voran, während alle Habiticaner zusammenarbeiten, um unsere geliebte Heimat wieder aufzubauen.",
"questDysheartenerBossRageTitle": "Niederschmetternder Herzschmerz", "questDysheartenerBossRageTitle": "Niederschmetternder Herzschmerz",
"questDysheartenerBossRageDescription": "Die Anzeige für den Raserei-Angriff füllt sich, wenn Habiticaner ihre Tagesaufgaben nicht abhaken. Sobald sie gefüllt ist, wird der Entmutiger seine Niederschmetternde Herzschmerz-Attacke über einem von Habitica's Ladenbesitzern entfesseln, also strengt Euch an und erledigt Eure Aufgaben!", "questDysheartenerBossRageDescription": "Die Anzeige für den Raserei-Angriff füllt sich, wenn Habiticaner ihre Tagesaufgaben nicht abhaken. Sobald sie gefüllt ist, wird der Entmutiger seine Niederschmetternde Herzschmerz-Attacke über einem von Habitica's Ladenbesitzern entfesseln, also strengt Euch an und erledigt Eure Aufgaben!",
"questDysheartenerBossRageSeasonal": "`Der Entmutiger entfesselt NIEDERSCHMETTERNDEN HERZSCHMERZ!`\n\nOh nein! Nachdem er sich an unseren unerledigten Tagesaufgaben gelabt hat, hat der Entmutiger genügend Stärke gesammelt, um seine Niederschmetternde Herzschmerz-Attacke einzusetzen. Mit einem schrillen Aufschrei treffen seine stacheligen Vorderbeine den Pavillon, unter dem der Jahreszeitenmarkt liegt! Die markerschütternde Explosion aus Magie lässt das Holz zersplittern, und die Jahreszeitenzauberin wird bei dem Anblick von Trauer überwältigt.\n\nRasch, lasst uns unsere Tagesaufgaben erledigen, damit das Biest kein weiteres Mal zuschlagen kann!", "questDysheartenerBossRageSeasonal": "`Der Entmutiger entfesselt NIEDERSCHMETTERNDEN HERZSCHMERZ!`\n\nOh nein! Nachdem er sich an unseren unerledigten Tagesaufgaben labte, hat der Entmutiger genügend Stärke gesammelt, um seine Niederschmetternde Herzschmerz-Attacke einzusetzen. Mit einem schrillen Aufschrei treffen seine stacheligen Vorderbeine den Pavillon, unter dem der Jahreszeitenmarkt liegt! Die markerschütternde Explosion aus Magie lässt das Holz zersplittern, und die Jahreszeitenzauberin wird bei dem Anblick von Trauer überwältigt.\n\nRasch, lasst uns unsere Tagesaufgaben erledigen, damit das Biest kein weiteres Mal zuschlagen kann!",
"seasonalShopRageStrikeHeader": "Der Jahreszeitenmarkt wurde angegriffen!", "seasonalShopRageStrikeHeader": "Der Jahreszeitenmarkt wurde angegriffen!",
"seasonalShopRageStrikeLead": "Leslie wurde das Herz gebrochen!", "seasonalShopRageStrikeLead": "Leslie wurde das Herz gebrochen!",
"seasonalShopRageStrikeRecap": "Am 21. Februar war unsere geschätzte Leslie, die Jahreszeitenzauberin, am Boden zerstört, als der Entmutiger den Jahreszeitenmarkt zerstörte. Schnell, erledigt Eure Aufgaben, um das Monster zu bekämpfen und beim Wiederaufbau zu helfen!", "seasonalShopRageStrikeRecap": "Am 21. Februar war unsere geschätzte Leslie, die Jahreszeitenzauberin, am Boden zerstört, als der Entmutiger den Jahreszeitenmarkt zerstörte. Schnell, erledigt Eure Aufgaben, um das Monster zu bekämpfen und beim Wiederaufbau zu helfen!",
@@ -588,7 +588,7 @@
"questsRageStrikeHeader": "Der Questladen wurde angegriffen!", "questsRageStrikeHeader": "Der Questladen wurde angegriffen!",
"questsRageStrikeLead": "Ian wurde das Herz gebrochen!", "questsRageStrikeLead": "Ian wurde das Herz gebrochen!",
"questsRageStrikeRecap": "Am 6. März war unser wundervoller Ian, der Questmeister, tief erschüttert, als der Entmutiger den Boden rund um den Questladen zerstörte. Schnell, erledigt Eure Aufgaben, um das Monster zu bekämpfen und beim Wiederaufbau zu helfen!", "questsRageStrikeRecap": "Am 6. März war unser wundervoller Ian, der Questmeister, tief erschüttert, als der Entmutiger den Boden rund um den Questladen zerstörte. Schnell, erledigt Eure Aufgaben, um das Monster zu bekämpfen und beim Wiederaufbau zu helfen!",
"questDysheartenerBossRageMarket": "`Der Entmutiger entfesselt NIEDERSCHMETTERNDEN HERZSCHMERZ!`\n\nHilfe! Nachdem er sich an unseren unerledigten Tagesaufgaben gelabt hat, lässt der Entmutiger eine weitere Niederschmetternde Herzschmerz-Attacke los, die die Wände des Markts zerbersten lässt und ihn dem Erboden gleich macht! Unter den herabfallenden Steinen weint Alex der Händler um seine von der Zerstörung betroffenen Waren.\n\nWir dürfen nicht zulassen, dass dies noch einmal passiert! Sorgt dafür, dass alle Eure Tagesaufgaben erledigt werden, bevor der Entmutiger zu seinem letzten Schlag ansetzt.", "questDysheartenerBossRageMarket": "`Der Entmutiger entfesselt NIEDERSCHMETTERNDEN HERZSCHMERZ!`\n\nHilfe! Nachdem er sich an unseren unerledigten Tagesaufgaben gelabt hat, lässt der Entmutiger eine weitere Niederschmetternde Herzschmerz-Attacke los, welche die Wände des Markts zerbersten lässt und ihn dem Erboden gleich macht! Unter den herabfallenden Steinen weint Alex der Händler um seine von der Zerstörung betroffenen Waren.\n\nWir dürfen nicht zulassen, dass dies noch einmal passiert! Sorgt dafür, dass alle Eure Tagesaufgaben erledigt werden, bevor der Entmutiger zu seinem letzten Schlag ansetzt.",
"questDysheartenerBossRageQuests": "`Der Entmutiger entfesselt NIEDERSCHMETTERNDEN HERZSCHMERZ!`\n\nAaaah! Wir haben schon wieder nicht alle Tagesaufgaben erledigt, und der Entmutiger hat genügend Energie für einen letzten Schlag gegen unsere geliebten Ladenbesitzer gesammelt. Die Landschaft um Ian den Questmeister wurde von seiner Niederschmetternden Herzschmerz-Attacke förmlich zerrissen, und Ian bis ins Mark erschüttert von dem schrecklichen Anblick. Wir sind so nah dran, dieses Monster zu besiegen... Beeilt Euch! Gebt jetzt nicht auf!", "questDysheartenerBossRageQuests": "`Der Entmutiger entfesselt NIEDERSCHMETTERNDEN HERZSCHMERZ!`\n\nAaaah! Wir haben schon wieder nicht alle Tagesaufgaben erledigt, und der Entmutiger hat genügend Energie für einen letzten Schlag gegen unsere geliebten Ladenbesitzer gesammelt. Die Landschaft um Ian den Questmeister wurde von seiner Niederschmetternden Herzschmerz-Attacke förmlich zerrissen, und Ian bis ins Mark erschüttert von dem schrecklichen Anblick. Wir sind so nah dran, dieses Monster zu besiegen... Beeilt Euch! Gebt jetzt nicht auf!",
"questDysheartenerDropHippogriffPet": "Hoffnungsfroher Hippogreif (Haustier)", "questDysheartenerDropHippogriffPet": "Hoffnungsfroher Hippogreif (Haustier)",
"questDysheartenerDropHippogriffMount": "Hoffnungsfroher Hippogreif (Reittier)", "questDysheartenerDropHippogriffMount": "Hoffnungsfroher Hippogreif (Reittier)",
@@ -706,7 +706,7 @@
"questWindupText": "Zähme die zankenden Zahnräder", "questWindupText": "Zähme die zankenden Zahnräder",
"questWindupBoss": "Klangton", "questWindupBoss": "Klangton",
"questWindupCompletion": "Während Du den Attacken ausweichst, fällt Dir etwas seltsames auf: ein gestreifter Messingschwanz ragt aus dem Fahrgestell des Roboters. Du greifst mit einer raschen Hand inmitten des laufenden Robotergetriebes und ziehst... ein zitterndes, aufziehbares Tiger-Junges heraus. Es schmiegt sich an deine Kleidung.<br><br>Der Uhrwerk-Roboter hört umgehend auf um sich zu schlagen und lächelt, seine Zahnräder klicken zurück an ihren Platz. \"Kä-Kä-Kätzchen! Kätzchen in meinem Getriebe!\"<br><br>\"Großartig\", sagt Großmächtig errötend. \"Ich habe hart an diesen Aufzieh-Haustier-Tränken gearbeitet. Ich habe wohl etwas die Übersicht über meine Neuerfindungen verloren. Wisst Ihr, ich habe mein tägliches \"Werkstatt aufräumen\" in letzter Zeit öfter verpasst...\"<br><br>Du folgst dem Bastler und Klangton nach drinnen. Teile, Werkzeug und Tränke bedecken jede Oberfläche. Großmächtig nimmt deine Uhr, doch gibt dir einige Tränke.<br><br>\"Hier, nimm diese. Offensichtlich werden sie bei Dir besser aufgehoben sein!\"", "questWindupCompletion": "Während Du den Attacken ausweichst, fällt Dir etwas seltsames auf: ein gestreifter Messingschwanz ragt aus dem Fahrgestell des Roboters. Du greifst mit einer raschen Hand inmitten des laufenden Robotergetriebes und ziehst... ein zitterndes, aufziehbares Tiger-Junges heraus. Es schmiegt sich an deine Kleidung.<br><br>Der Uhrwerk-Roboter hört umgehend auf um sich zu schlagen und lächelt, seine Zahnräder klicken zurück an ihren Platz. \"Kä-Kä-Kätzchen! Kätzchen in meinem Getriebe!\"<br><br>\"Großartig\", sagt Großmächtig errötend. \"Ich habe hart an diesen Aufzieh-Haustier-Tränken gearbeitet. Ich habe wohl etwas die Übersicht über meine Neuerfindungen verloren. Wisst Ihr, ich habe mein tägliches \"Werkstatt aufräumen\" in letzter Zeit öfter verpasst...\"<br><br>Du folgst dem Bastler und Klangton nach drinnen. Teile, Werkzeug und Tränke bedecken jede Oberfläche. Großmächtig nimmt deine Uhr, doch gibt dir einige Tränke.<br><br>\"Hier, nimm diese. Offensichtlich werden sie bei Dir besser aufgehoben sein!\"",
"questWindupNotes": "*Habit City* ist selten ruhig, doch auf diese missgestimmte Katzenmusik warst Du nicht vorbereitet: Eine Kakophonie aus Knarzen, Quietschen und Kreischen, drang aus dem Laden *Good Timekeeping*, Habiticas bestem Uhrmacher. Du seufzt--Du wolltest doch nur Deine Uhr reparieren lassen. \"Great and Powerful\", oder \"Großmächtig\", wie der Uhrmacher genannt wird, kommt Dir bereits entgegen gestolpert, direkt gefolgt von einem klirrenden Kupfergiganten. <br><br> \"Kä-! Kä-! Kä-!\" klingelt er, die Arme umher werfend. Sein Getriebe malt und quietscht unter Protest.<br><br>\"Mein Roboter \"Klangton\" ist verrückt geworden! Er will mich umbringen!\" kreischt der angeblich mächtige Uhrmacher.<br><br>Sogar mit einer kaputten Uhr weißt Du wann die Zeit reif ist, zu kämpfen. Du stürzt voran, um den panischen Uhrmacher zu beschützen. @Vikte und @a_diamond kommen ebenfalls zur Hilfe!<br><br>\"Kä-! Kä-! Kä-!\", singt Klangton bei jedem Schlag. \"Miau!\"<br><br>Moment mal, war das ein mechanischen Miauen inmitten der mörderischen Monotonie?", "questWindupNotes": "*Habit City* ist selten ruhig, doch auf diese missgestimmte Katzenmusik warst Du nicht vorbereitet: Eine Kakophonie aus Knarzen, Quietschen und Kreischen, drang aus dem Laden *Good Timekeeping*, Habiticas bestem Uhrmacher. Du seufzt--Du wolltest doch nur Deine Uhr reparieren lassen. \"Great and Powerful\", oder \"Der Großmächtige\", wie der Uhrmacher genannt wird, kommt Dir bereits entgegen gestolpert, direkt gefolgt von einem klirrenden Kupfergiganten. <br><br> \"Ki-! Ki-! Ki!\" klingelt er, die Arme umher werfend. Sein Getriebe malt und quietscht unter Protest.<br><br>\"Mein Roboter \"Klangton\" ist verrückt geworden! Er will mich umbringen!\" kreischt der angeblich mächtige Uhrmacher.<br><br>Sogar mit einer kaputten Uhr weißt Du wann die Zeit reif ist, zu kämpfen. Du stürzt voran, um den panischen Uhrmacher zu beschützen. @Vikte und @a_diamond kommen ebenfalls zu Hilfe!<br><br>\"Ki-! Ki-! Ki!\", singt Klangton bei jedem Schlag. \"Miau!\"<br><br>Moment mal, war das ein mechanisches Miauen inmitten der mörderischen Monotonie?",
"questTurquoiseText": "Türkisgrüne Schatzsuchtortur", "questTurquoiseText": "Türkisgrüne Schatzsuchtortur",
"questTurquoiseUnlockText": "Schaltet den Kauf von Türkisen Schlüpfelixieren auf dem Marktplatz frei", "questTurquoiseUnlockText": "Schaltet den Kauf von Türkisen Schlüpfelixieren auf dem Marktplatz frei",
"questTurquoiseDropTurquoisePotion": "Türkises Schlüpfelixier", "questTurquoiseDropTurquoisePotion": "Türkises Schlüpfelixier",
@@ -846,5 +846,19 @@
"questPlatypusRageDescription": "Diese Leiste füllt sich, wenn Du Deine Tagesaufgaben nicht erfüllst. Wenn sie voll ist, nimmt Das Perfektionisten Schnabeltier deiner Party einige ihrer MP weg!", "questPlatypusRageDescription": "Diese Leiste füllt sich, wenn Du Deine Tagesaufgaben nicht erfüllst. Wenn sie voll ist, nimmt Das Perfektionisten Schnabeltier deiner Party einige ihrer MP weg!",
"questPlatypusRageEffect": "Das Perfektionisten Schnabeltier taucht unter Wasser und spritzt dich naß! Die MP deiner Party sind reduziert!", "questPlatypusRageEffect": "Das Perfektionisten Schnabeltier taucht unter Wasser und spritzt dich naß! Die MP deiner Party sind reduziert!",
"questPlatypusDropPlatypusEgg": "Schnabeltier (Ei)", "questPlatypusDropPlatypusEgg": "Schnabeltier (Ei)",
"questPlatypusUnlockText": "Schält Schnabeltier Eier zum Kauf im Marktplatz frei" "questPlatypusUnlockText": "Schält Schnabeltier Eier zum Kauf im Marktplatz frei",
"questOtterNotes": "To-Do-Listen sind großartig! Man kann Stunden damit verbringen, jeden Schritt, den man tun muss, akribisch zu dokumentieren und sich produktiv zu fühlen, ohne diese Dinge tatsächlich zu tun. Deine dreiseitige Liste wird in die Tasche gesteckt. Zeit für einen erfrischenden Spaziergang!<br><br>Du machst dich auf den Weg zum Routinen-Fluss, um einen Spaziergang am Ufer zu machen. Das ist genau das, was Du brauchst, um endlich loszulegen! Zeit, Deine To-Do-Liste hervorzuholen und - ach! Ein Windstoß lässt Dir die Liste aus der Hand und direkt in Richtung Wasser fliegen!<br><br>Kurz bevor das Papier ins Wasser fällt, taucht ein Otterkopf auf und fängt das Blatt ab. Puh! Er hält die Liste in seinen Pfoten und ein schelmisches Grinsen breitet sich auf seinem Gesicht aus... Uh-oh.<br><br>„Hmmm“, summt er und wendet das Papier, um Deine Liste zu lesen. „Sieht aus, als bräuchtest Du Hilfe beim Setzen von Prioritäten.“ Ritsch.<br><br>Der Otter hat gerade Deine sorgfältig erstellte Liste in Stücke gerissen! „Wenn Du das alles erledigen willst, musst Du Dich zuerst entscheiden, was das Wichtigste ist“, sagt er und wirft die Punkte auf Deiner Liste Stück für Stück in den Wind.",
"questOtterCompletion": "Als Du es geschafft hast, die Teile Deiner Liste einzufangen, begannst du, sie danach zu sortieren, welche Aufgaben am wichtigsten sind, und hast am Ende einen recht handhabbaren Startpunkt gefunden!<br><br>„Jetzt verstehe ich!“, sagst Du dem Otter, „dieser Quatsch hat mir tatsächlich geholfen, darüber nachzudenken, welche Aufgaben ich priorisieren muss.“<br><br>Der Otter spritzt herum und reibt sich vergnügt die Wangen: „Ich bin froh, dass mein kleiner Plan Dich dazu gebracht hat, anders über Deine Aufgaben nachzudenken.“ Er taucht unter und in der Nähe wieder auf. \"Denke daran, deine Listen machbar zu halten. Belohnungen helfen auch, also nimm die hier!\"",
"questPlatypusCompletion": "Nach einem erschöpfenden hin und her von Wasserstrahlen und einigen aufmunternden Worten deinerseits hält das Schnabeltier endlich inne und kommt seufzend an die Oberfläche.<br><br>„Da hast du vielleicht recht. Wenn ich Perfektion verlange, werde ich nie fertig! Ich kann ja immer noch Anpassungen vornehmen. Du scheinst dich mit Perfektionismus auszukennen.“<br><br>Du siehst auf dein durchnässtes Arbeitsblatt. „Ja …“<br><br>„Tut mir leid“, sagt das Schnabeltier. „Als Entschuldigung dafür, dass dein Aufsatz nass geworden ist, nimm bitte ein paar Eier, die ich im Schlamm gefunden habe.“",
"questOpalNotes": "Habiticas Gelehrte haben lange nach dem sagenumwobenen magischen Opal-Schlüpfelixier gesucht. Ein Trank, so machtvoll, dass er Haustieren und deren Reittieren eine feurige Farbe und Glanz verleiht wie kein anderer Edelstein oder Edelmetall. Gerüchten zufolge soll die Magie der Opale sogar Planung, Einsicht und Kreativität verbessern. Welchen Auftrieb dir das bei deinen Aufgaben geben würde!<br><br>Nach langem Suchen haben Sie vielleicht endlich die Antwort gefunden. Opalelixiere erfordern rohe Opale, auf welche die magischen Runen von Waage und Merkur geschmiedet werden. Diese antiken Gegenstände können nur an einem Ort gefunden werden … den gefährlichen Ruinen der verlorenen Stadt am Rande der Zeitwüste.<br><br>Du erreichst die Ruinen, nachdem du tagelang auf deinen stärksten Reittier durch das raue und abgelegene Gelände geritten bist. Zwischen den sonnengebleichten und zerbrochenen Steinen siehst du einen hellen Schimmer. Die Suche beginnt!",
"questJadeCompletion": "Nach unzähligen Rückschlägen hast du es irgendwie geschafft, den Jadebrocken auf den Gipfel des Berges zu rollen! Die Steinfigur holt dich ein und lächelt. Sie gibt dem Brocken einen leichten Schubs, und du siehst entsetzt zu, wie er wieder nach unten rollt.<br><br>„Warum hast du das gemacht? Jetzt muss das jemand nochmal machen!“, beklagst du dich.<br><br>„Nur weil du etwas mehr als einmal tun musst, heißt das nicht, dass deine Leistungen bedeutungslos sind“, sagt die Steinfigur. „Konzentriere dich jetzt auf das Erreichte und erfreue dich an einer Belohnung!“<br><br>Du schreckst von deiner Couch hoch, als dein Handy auf den Boden fällt. Neben ihm stehen drei Flaschen flüssige Jade! Vielleicht ist es Zeit, das Geschirr von heute abzuwaschen und dann eine Pause einzulegen, um zu sehen, wie diese Tränke bei ein paar Haustier-Eiern wirken …",
"questAlpacaNotes": "Die Sonne brennt auf dich hernieder, während du die felsigen Pfade des Mäandergebirges hinaufwanderst. Seit Monaten planst du diese Expedition für deine Freunde und hast jeden Aspekt der Reise recherchiert. Das Gewicht der Vorräte auf deinem Rücken ist so schwer zu tragen, dass sich jeder Schritt eher wie eine Strafe als ein Abenteuer anfühlt.<br><br>Du hörst ein leises Knirschen von Hufen auf dem Pfad hinter dir. Ein flauschiges Alpaka nähert sich mit einem gigantischen Stapel Gepäck auf dem Rücken.<br><br>„Sieht aus, als würdest du dich ganz schön abschleppen, Kumpel, und du trägst nur einen kleinen Rucksack!“, sagt es, als es vorbeigeht.<br><br>„Bei dir sieht es so einfach aus“, seufzst du. „Ich habe diese Reise so lange geplant, aber jetzt, wo wir hier sind, macht es mir nicht mal mehr Spaß …“<br><br>„Lass dich nicht unterkriegen“, schnaubt das Alpaka. „Ich werde dir eine Lektion erteilen, die ich vor langer Zeit gelernt habe!“ Es bockt und plötzlich fliegt ein zusammengerollter Schlafsack auf dich zu! Wie hilft das nochmal?!",
"questPlatypusNotes": "Es ist ein wundervoller Tag am Conquest Creek, der durch das Arbeitsblatt in deiner Hand nun doch ziemlich mies wird. Warum werden tolle Abenteuer immer durch Hausaufgaben ruiniert? Nach fünf Fragen zu Fluss-Ökosystemen, musst du dich auch noch mit einem Aufsatz herumschlagen.<br><br>„Beschreiben Sie, wie sich ein Tier an das Leben im Fluss anpassen könnte? Puh, keine Ahnung…“<br><br>Nachdem du 30 Minuten lang hoffnungslos festsitzt und nicht einmal weißt, wie du überhaupt anfangen sollst, hättest du eine Menge frustriert klingendes Platschen am Ufer entlang.<br><br>„Auuh“, blubbert eine Stimme direkt unter der Oberfläche. Ein erschöpft aussehendes Schnabeltier taucht auf. „Dieser Bau will einfach nicht zusammenpassen! Jedes Mal, wenn ich anfange, sieht es einfach falsch aus.“ Es taucht wieder unter die Oberfläche und sein breiter, flacher Schwanz spritzt dir mächtig ins Gesicht.<br><br>„Warte, mach nicht alles kaputt –“, rufst du, als dich ein weiterer Schwall Wasser aus dem Bach trifft. Vielleicht kannst du helfen und dich dabei inspirieren lassen!",
"questOpalText": "Die Legende der Obskuren Opale",
"questOpalCompletion": "Schließlich findest du, müde und staubig, die letzten Runen und den Opalstein, die du brauchst, um das magische Schlüpfelixier zu schmieden.<br><br>Du beginnst mit dem Schmieden, sobald du wieder in Habiticas Hauptstadt bist. Die Kraft der Runen und Opale erfüllt dein Labor mit Licht in den Farben des Regenbogens! Im Handumdrehen hast du drei Elixiere geschmiedet und freust dich darauf, neue, farbenfrohe Freunde auszubrüten.",
"questOpalCollectLibraRunes": "Waage-Rune",
"questOpalCollectMercuryRunes": "Merkur-Rune",
"questOpalCollectOpalGems": "Opal-Edelstein",
"questOpalDropOpalPotion": "Opal-Schlüpfelixier",
"questOpalUnlockText": "Schaltet im Markt das Opal-Schlüpfelixier zum Kauf frei"
} }

View File

@@ -55,7 +55,6 @@
"APIToken": "API-Token (Das ist ein Passwort die obige Warnung gilt auch hier!)", "APIToken": "API-Token (Das ist ein Passwort die obige Warnung gilt auch hier!)",
"showAPIToken": "API-Token anzeigen", "showAPIToken": "API-Token anzeigen",
"hideAPIToken": "API-Token verbergen", "hideAPIToken": "API-Token verbergen",
"APITokenWarning": "Wenn Du einen neuen API-Token brauchst (z.B. weil Du ihn versehentlich geteilt hast), schreibe eine E-Mail an <%= hrefTechAssistanceEmail %> mit Deiner Benutzer ID und dem aktuellen Schlüssel. Sobald er zurückgesetzt ist, wirst Du Dich auf der Webseite und der mobilen App aus- und neu einloggen müssen und den Schlüssel in jedem anderen Habitica Tool, das Du nutzt, einstellen müssen.",
"thirdPartyApps": "Apps von Drittanbietern", "thirdPartyApps": "Apps von Drittanbietern",
"resetDo": "Ja, setzt mein Konto jetzt zurück!", "resetDo": "Ja, setzt mein Konto jetzt zurück!",
"resetComplete": "Zurückgesetzt!", "resetComplete": "Zurückgesetzt!",
@@ -117,7 +116,7 @@
"generate": "Erstelle", "generate": "Erstelle",
"getCodes": "Codes erhalten", "getCodes": "Codes erhalten",
"webhooks": "WebHooks", "webhooks": "WebHooks",
"webhooksInfo": "WebHooks bieten Entwicklern die Möglichkeit, Benachrichtigngen zu erhalten, wenn eine bestimmte Aktion durchgeführt wird, z. B. das Bewerten oder Aktualisieren einer Aufgabe oder das Senden einer Nachricht in einer Gruppe. Indem du einen WebHook erstellst, kannst du Änderungen in Habitica wahrnehmen und Anwendungen entwickeln, die auf diese Änderungen reagieren. <br><br> Weitere Informationen und Beispiele findest Du bei den <a target=\"_blank\" href=\"https://habitica.fandom.com/wiki/Webhooks\">API Docs</a>.", "webhooksInfo": "WebHooks bieten Entwicklern die Möglichkeit, Benachrichtigungen zu erhalten, wenn eine bestimmte Aktion durchgeführt wird, z. B. das Bewerten oder Aktualisieren einer Aufgabe oder das Senden einer Nachricht in einer Gruppe. Indem du einen WebHook erstellst, kannst du Änderungen in Habitica wahrnehmen und Anwendungen entwickeln, die auf diese Änderungen reagieren. <br><br> Weitere Informationen und Beispiele findest Du bei den <a target=\"_blank\" href=\"https://habitica.com/apidoc/#api-Webhook-AddWebhook\">API Docs</a>.",
"enabled": "Aktiviert", "enabled": "Aktiviert",
"webhookURL": "WebHook-URL", "webhookURL": "WebHook-URL",
"invalidUrl": "Ungültige URL", "invalidUrl": "Ungültige URL",
@@ -197,7 +196,7 @@
"giftSubscriptionRateText": "<strong>$<%= price %> $(USD)</strong> für <strong><%= months %> Monate</strong>", "giftSubscriptionRateText": "<strong>$<%= price %> $(USD)</strong> für <strong><%= months %> Monate</strong>",
"transaction_admin_update_balance": "<b>Admin</b> gegeben", "transaction_admin_update_balance": "<b>Admin</b> gegeben",
"transaction_admin_update_hourglasses": "<b>Admin</b> aktualisiert", "transaction_admin_update_hourglasses": "<b>Admin</b> aktualisiert",
"transaction_create_bank_challenge": "Bankherausforderung <b>erstellt</b>", "transaction_create_bank_challenge": "Bank-Herausforderung <b>erstellt</b>",
"passwordIssueLength": "Passwörter müssen zwischen 8 und 64 Zeichen lang sein.", "passwordIssueLength": "Passwörter müssen zwischen 8 und 64 Zeichen lang sein.",
"timestamp": "Zeitstempel", "timestamp": "Zeitstempel",
"amount": "Menge", "amount": "Menge",
@@ -242,7 +241,7 @@
"addWebhook": "Webhook hinzufügen", "addWebhook": "Webhook hinzufügen",
"changeEmailDisclaimer": "Dies ist die Email Adresse, die du benutzt, um dich bei Habitica anzumelden und um Benachrichtigungen zu erhalten.", "changeEmailDisclaimer": "Dies ist die Email Adresse, die du benutzt, um dich bei Habitica anzumelden und um Benachrichtigungen zu erhalten.",
"changeDisplayNameDisclaimer": "Dies ist der Name, der für deinen Avatar in Habitica angezeigt wird.", "changeDisplayNameDisclaimer": "Dies ist der Name, der für deinen Avatar in Habitica angezeigt wird.",
"changePasswordDisclaimer": "Passwort muß 8 Zeichen oder länger sein. Wir empfehlen ein starkes Passwort, das du sonst nirgends verwendest.", "changePasswordDisclaimer": "Das Ändern deines Passwortes wird dich von all deinen Geräten und Drittanbieter-Anwendungen abmelden.",
"dateFormatDisclaimer": "Passe die Datumsformatierung in Habitica an.", "dateFormatDisclaimer": "Passe die Datumsformatierung in Habitica an.",
"enableAudio": "Aktiviere Audio", "enableAudio": "Aktiviere Audio",
"playDemoAudio": "Spiele Demo ab", "playDemoAudio": "Spiele Demo ab",
@@ -256,7 +255,7 @@
"connected": "Verbunden", "connected": "Verbunden",
"connect": "Verbinden", "connect": "Verbinden",
"changeClassDisclaimer": "Bei Änderung deiner Klasse bekommst du alle deine vorhandenen Attributspunkte erstattet. Passe deine Attributspunkte im Attributwerte-Abschnitt deines Profils an, sobald du eine neue Klasse gewählt hast.", "changeClassDisclaimer": "Bei Änderung deiner Klasse bekommst du alle deine vorhandenen Attributspunkte erstattet. Passe deine Attributspunkte im Attributwerte-Abschnitt deines Profils an, sobald du eine neue Klasse gewählt hast.",
"APITokenDisclaimer": "<b>Dein API Token ist wie ein Passwort. Teile es nicht öffentlich.</b>Es kann sein, daß du gelegentlich nach deiner User ID gefragt wirst, aber poste niemals dein API Token, wo andere es sehen können, einschließlich Github.<br><br><b>Hinweis:</b> Wenn du ein neues API Token brauchst (z.B., wenn du es aus Versehen geteilt hast), schreib eine Email an <a href='mailto:admin@habitica.com' target='_blank'>admin@habitica.com</a> mit deiner User ID und deinem aktuellen Token. Wenn es dann zurückgesetzt ist, musst du alles wieder neu authorisieren, indem du dich auf der Website und der mobilen App abmeldest und das neue Token bei allen anderen Habitica Tools, die du nutzt, angibst.", "APITokenDisclaimer": "<b>Dein API-Schlüssel ist wie ein Passwort. Teile ihn niemals öffentlich.</b> Du wirst ggf. nach deiner Benutzer-ID gefragt, aber schreibe deinen API-Schlüssel niemals dort, wo andere ihn sehen könnten bspw. Github.<br><br><b>Hinweis:</b> Falls du ein neues API-Schlüssel brauchst (z.B., weil du ihn versehentlich geteilt hast), kannst du dein Passwort ändern, um ihn zurückzusetzen. Einmal zurückgesetzt, musst du dich auf all deinen Geräten, auf denen du Habitica verwendest, erneut anmelden und deinen neuen API-Schlüssel in Drittanbieter-Anwendungen angeben, die du verwendest.",
"thirdPartyTools": "Drittanbieter Apps, Erweiterungen, und alle möglichen anderen Tools, die du mit deinem Account nutzen kannst, findest du im <a href='https://habitica.fandom.com/wiki/Extensions,_Add-Ons,_and_Customizations' target='_blank'>Habitica Wiki</a>.", "thirdPartyTools": "Drittanbieter Apps, Erweiterungen, und alle möglichen anderen Tools, die du mit deinem Account nutzen kannst, findest du im <a href='https://habitica.fandom.com/wiki/Extensions,_Add-Ons,_and_Customizations' target='_blank'>Habitica Wiki</a>.",
"transaction_subscription_bonus": "<b>Abonnement</b>-Bonus" "transaction_subscription_bonus": "<b>Abonnement</b>-Bonus"
} }

View File

@@ -21,7 +21,7 @@
"subGemName": "Abonnenten-Edelsteine", "subGemName": "Abonnenten-Edelsteine",
"maxBuyGems": "Du hast diesen Monat schon die erlaubte Menge Edelsteine gekauft. Innerhalb der ersten drei Tage des folgenden Monats werden neue verfügbar sein. Danke für Dein Abonnement!", "maxBuyGems": "Du hast diesen Monat schon die erlaubte Menge Edelsteine gekauft. Innerhalb der ersten drei Tage des folgenden Monats werden neue verfügbar sein. Danke für Dein Abonnement!",
"timeTravelers": "Mysteriöse Zeitreisende", "timeTravelers": "Mysteriöse Zeitreisende",
"timeTravelersPopoverNoSubMobile": "Sieht aus als bräuchtest Du eine Mystische Sanduhr um das Zeitportal zu öffnen und die Mysteriösen Zeitreisenden herbei zu rufen.", "timeTravelersPopoverNoSubMobile": "Abonnenten erhalten jeden Monat eine Mystische Sanduhr für den Laden der Zeitreisenden!",
"timeTravelersPopover": "Deine Mystische Sanduhr hat unser Zeitportal geöffnet! Wähle etwas aus, was wir für Dich aus der Vergangenheit oder Zukunft holen sollen.", "timeTravelersPopover": "Deine Mystische Sanduhr hat unser Zeitportal geöffnet! Wähle etwas aus, was wir für Dich aus der Vergangenheit oder Zukunft holen sollen.",
"mysterySetNotFound": "Überraschungsset nicht gefunden, oder Set wurde bereits erworben.", "mysterySetNotFound": "Überraschungsset nicht gefunden, oder Set wurde bereits erworben.",
"mysteryItemIsEmpty": "Mysteriöse Gegenstände sind leer", "mysteryItemIsEmpty": "Mysteriöse Gegenstände sind leer",
@@ -264,5 +264,7 @@
"mysterySet202503": "Jade Juggernaut Set", "mysterySet202503": "Jade Juggernaut Set",
"mysterySet202504": "Scheues Yeti Set", "mysterySet202504": "Scheues Yeti Set",
"mysterySet202505": "Hochfliegender Schwalbenschwanz Set", "mysterySet202505": "Hochfliegender Schwalbenschwanz Set",
"mysterySet202506": "Sonnenschein Set" "mysterySet202506": "Sonnenschein Set",
"mysterySet202507": "Draufgängerisches Skater Set",
"mysterySet202508": "Strahlendes Klingen Set"
} }

View File

@@ -0,0 +1,7 @@
{
"adminPanel": "Admin Panel",
"siteBlockers": "Site Blockers",
"newsroom": "Newsroom",
"adminBlockerTypeDescription": "<b>IP-Address</b> - Block access for a specific IP-Address\n\nClient - Block access for a client based on the \"x-client\" header.\n\nE-Mail - Blocks e-mails from being used for signup.",
"adminBlockerAreaDescription": "A blocker can either apply to the full site, completely blocking any access. Or it can apply to purchases, which still allows the site to be accessed."
}

View File

@@ -1031,6 +1031,14 @@
"backgroundSummerSeashoreText": "Summer Seashore", "backgroundSummerSeashoreText": "Summer Seashore",
"backgroundSummerSeashoreNotes": "Catch a wave at a Summer Seashore.", "backgroundSummerSeashoreNotes": "Catch a wave at a Summer Seashore.",
"backgrounds072025": "SET 134: Released July 2025",
"backgroundSirensLairText": "Siren's Lair",
"backgroundSirensLairNotes": "Dare to dive into a Sirens Lair.",
"backgrounds082025": "SET 135: Released August 2025",
"backgroundSunnyStreetWithShopsText": "Sunny Street with Shops",
"backgroundSunnyStreetWithShopsNotes": "Enjoy the sights and sounds of a Sunny Street with Shops.",
"timeTravelBackgrounds": "Steampunk Backgrounds", "timeTravelBackgrounds": "Steampunk Backgrounds",
"backgroundAirshipText": "Airship", "backgroundAirshipText": "Airship",
"backgroundAirshipNotes": "Become a sky sailor on board your very own Airship.", "backgroundAirshipNotes": "Become a sky sailor on board your very own Airship.",

View File

@@ -109,12 +109,14 @@
"tweet": "Tweet", "tweet": "Tweet",
"checkOutMobileApps": "Check out our mobile apps!", "checkOutMobileApps": "Check out our mobile apps!",
"missingAuthHeaders": "Missing authentication headers.", "missingAuthHeaders": "Missing authentication headers.",
"missingClientHeader": "Missing x-client headers.",
"missingUsernameEmail": "Missing username or email.", "missingUsernameEmail": "Missing username or email.",
"missingEmail": "Missing email.", "missingEmail": "Missing email.",
"missingUsername": "Missing username.", "missingUsername": "Missing username.",
"missingPassword": "Missing password.", "missingPassword": "Missing password.",
"missingNewPassword": "Missing new password.", "missingNewPassword": "Missing new password.",
"invalidEmailDomain": "You cannot register with emails with the following domains: <%= domains %>", "invalidEmailDomain": "You cannot register with emails with the following domains: <%= domains %>",
"emailBlockedRegistration": "This E-Mail is blocked from registration. If you think this is a mistake, please contact us at admin@habitica.com.",
"wrongPassword": "Password is incorrect. If you forgot your password, click \"Forgot Password.\"", "wrongPassword": "Password is incorrect. If you forgot your password, click \"Forgot Password.\"",
"incorrectDeletePhrase": "Please type <%= magicWord %> in all capital letters to delete your account.", "incorrectDeletePhrase": "Please type <%= magicWord %> in all capital letters to delete your account.",
"incorrectResetPhrase": "Please type <%= magicWord %> in all capital letters to reset your account.", "incorrectResetPhrase": "Please type <%= magicWord %> in all capital letters to reset your account.",
@@ -131,7 +133,7 @@
"passwordReset": "If we have your email or username on file, instructions for setting a new password have been sent to your email.", "passwordReset": "If we have your email or username on file, instructions for setting a new password have been sent to your email.",
"invalidLoginCredentialsLong": "Uh-oh - your email address / username or password is incorrect.\n- Make sure they are typed correctly. Your username and password are case-sensitive.\n- You may have signed up with Facebook or Google-sign-in, not email so double-check by trying them.\n- If you forgot your password, click \"Forgot Password\".", "invalidLoginCredentialsLong": "Uh-oh - your email address / username or password is incorrect.\n- Make sure they are typed correctly. Your username and password are case-sensitive.\n- You may have signed up with Facebook or Google-sign-in, not email so double-check by trying them.\n- If you forgot your password, click \"Forgot Password\".",
"invalidCredentials": "There is no account that uses those credentials.", "invalidCredentials": "There is no account that uses those credentials.",
"accountSuspended": "This account, User ID \"<%= userId %>\", has been blocked for breaking the Community Guidelines (https://habitica.com/static/community-guidelines) or Terms of Service (https://habitica.com/static/terms). For details or to ask to be unblocked, please email our Community Manager at <%= communityManagerEmail %> or ask your parent or guardian to email them. Please include your @Username in the email.", "accountSuspended": "Your account @<%= username %> has been blocked. For additional information, or to request an appeal, email admin@habitica.com with your Habitica username or User ID.",
"accountSuspendedTitle": "Account has been suspended", "accountSuspendedTitle": "Account has been suspended",
"unsupportedNetwork": "This network is not currently supported.", "unsupportedNetwork": "This network is not currently supported.",
"cantDetachSocial": "Account lacks another authentication method; can't detach this authentication method.", "cantDetachSocial": "Account lacks another authentication method; can't detach this authentication method.",

View File

@@ -600,6 +600,8 @@
"weaponMystery202404Notes": "This staff will bestow upon you an ancient wisdom as ageless as the rocks and trees. Confers no benefit. April 2024 Subscriber Item.", "weaponMystery202404Notes": "This staff will bestow upon you an ancient wisdom as ageless as the rocks and trees. Confers no benefit. April 2024 Subscriber Item.",
"weaponMystery202408Text": "Arcane Aegis", "weaponMystery202408Text": "Arcane Aegis",
"weaponMystery202408Notes": "A magic bubble shield that protects you from enemy spells or helps you float in the air or water. Confers no benefit. August 2024 Subscriber Item.", "weaponMystery202408Notes": "A magic bubble shield that protects you from enemy spells or helps you float in the air or water. Confers no benefit. August 2024 Subscriber Item.",
"weaponMystery202508Text": "Brilliant Crimson Blade",
"weaponMystery202508Notes": "This spinning blade will terrify any monster or red Daily that crosses your path! Confers no benefit. August 2025 Subscriber Item.",
"weaponMystery301404Text": "Steampunk Cane", "weaponMystery301404Text": "Steampunk Cane",
"weaponMystery301404Notes": "Excellent for taking a turn about town. March 3015 Subscriber Item. Confers no benefit.", "weaponMystery301404Notes": "Excellent for taking a turn about town. March 3015 Subscriber Item. Confers no benefit.",
@@ -1734,6 +1736,10 @@
"armorArmoireGildedKnightsPlateNotes": "In this armor, you are nearly invincible. Your enemies will surely hear you roar! Increases Perception by <%= per %>. Enchanted Armoire: Gilded Knight Set (Item 2 of 3)", "armorArmoireGildedKnightsPlateNotes": "In this armor, you are nearly invincible. Your enemies will surely hear you roar! Increases Perception by <%= per %>. Enchanted Armoire: Gilded Knight Set (Item 2 of 3)",
"armorArmoireBeekeepersSuitText": "Beekeeping Suit", "armorArmoireBeekeepersSuitText": "Beekeeping Suit",
"armorArmoireBeekeepersSuitNotes": "Keep yourself safe as you look after your busy bumbles. Increases Constitution by <%= con %>. Enchanted Armoire: Beekeeper Set (Item 2 of 4)", "armorArmoireBeekeepersSuitNotes": "Keep yourself safe as you look after your busy bumbles. Increases Constitution by <%= con %>. Enchanted Armoire: Beekeeper Set (Item 2 of 4)",
"armorArmoireFlyFishingWadersText": "Fly Fishing Waders",
"armorArmoireFlyFishingWadersNotes": "Stay perfectly warm and dry when you wade into a stream, pond, lake, or river. Increases Strength and Constitution by <%= attrs %> each. Enchanted Armoire: Fly Fishing Set (Item 2 of 3)",
"armorArmoireRedWaistcoatText": "Red Waistcoat",
"armorArmoireRedWaistcoatNotes": "Look smart and stunning as you tackle your tasks. Theres something secret hidden in the vest pocket—what do you think it could be? Increases Constitution and Strength by <%= attrs %> each. Enchanted Armoire: Red Waistcoat Set (Item 2 of 2)",
"headgear": "helm", "headgear": "helm",
"headgearCapitalized": "Headgear", "headgearCapitalized": "Headgear",
@@ -2449,6 +2455,8 @@
"headMystery202503Notes": "This verdant hairdo perfectly suits a brave warrior and defender of the planet. Confers no benefit. March 2025 Subscriber Item.", "headMystery202503Notes": "This verdant hairdo perfectly suits a brave warrior and defender of the planet. Confers no benefit. March 2025 Subscriber Item.",
"headMystery202504Text": "Elusive Yeti Cowl", "headMystery202504Text": "Elusive Yeti Cowl",
"headMystery202504Notes": "Wear this mysterious visage to dwell undetected among the worlds most obscure cyptids. Confers no benefit. April 2025 Subscriber Item.", "headMystery202504Notes": "Wear this mysterious visage to dwell undetected among the worlds most obscure cyptids. Confers no benefit. April 2025 Subscriber Item.",
"headMystery202507Text": "Spunky Skater Cap",
"headMystery202507Notes": "Backwards hats are still cool, right? Confers no benefit. July 2025 Subscriber Item.",
"headMystery301404Text": "Fancy Top Hat", "headMystery301404Text": "Fancy Top Hat",
"headMystery301404Notes": "A fancy top hat for the finest of gentlefolk! January 3015 Subscriber Item. Confers no benefit.", "headMystery301404Notes": "A fancy top hat for the finest of gentlefolk! January 3015 Subscriber Item. Confers no benefit.",
@@ -2667,6 +2675,10 @@
"headArmoireGildedKnightsHelmNotes": "Shine as bright as the sun through the darkness of any battle. Increases Constitution by <%= con %>. Enchanted Armoire: Gilded Knight Set (Item 1 of 3)", "headArmoireGildedKnightsHelmNotes": "Shine as bright as the sun through the darkness of any battle. Increases Constitution by <%= con %>. Enchanted Armoire: Gilded Knight Set (Item 1 of 3)",
"headArmoireBeekeepersHatText": "Beekeeping Helmet and Veil", "headArmoireBeekeepersHatText": "Beekeeping Helmet and Veil",
"headArmoireBeekeepersHatNotes": "Keep your face safe as you tend to your buzzing buddies. Increases Perception by <%= per %>. Enchanted Armoire: Beekeeper Set (Item 1 of 4)", "headArmoireBeekeepersHatNotes": "Keep your face safe as you tend to your buzzing buddies. Increases Perception by <%= per %>. Enchanted Armoire: Beekeeper Set (Item 1 of 4)",
"headArmoireFlyFishingHatText": "Fly Fishing Hat",
"headArmoireFlyFishingHatNotes": "With a wide brim and spot to hold a spare lure, dont go fly fishing without this beauty. Increases Strength and Perception by <%= attrs %> each. Enchanted Armoire: Fly Fishing Set (Item 1 of 3)",
"headArmoireRedNewsieHatText": "Red Newsie Cap",
"headArmoireRedNewsieHatNotes": "Extra! Extra! Read all about it: this cap is comfortable, fashionable, and practical. Increases Perception and Intelligence by <%= attrs %> each. Enchanted Armoire: Red Waistcoat Set (Item 1 of 2)",
"offhand": "off-hand item", "offhand": "off-hand item",
"offHandCapitalized": "Off-Hand Item", "offHandCapitalized": "Off-Hand Item",
@@ -3000,6 +3012,8 @@
"shieldMystery202502Notes": "This Valentines Day and every day, may your heart be as light as these buoyant balloons. Confers no benefit. February 2025 Subscriber Item.", "shieldMystery202502Notes": "This Valentines Day and every day, may your heart be as light as these buoyant balloons. Confers no benefit. February 2025 Subscriber Item.",
"shieldMystery202506Text": "Solar Shine Shield", "shieldMystery202506Text": "Solar Shine Shield",
"shieldMystery202506Notes": "Dispel darkness and bestow warm and cheerful rays wherever you are. Confers no benefit. June 2025 Subscriber Item.", "shieldMystery202506Notes": "Dispel darkness and bestow warm and cheerful rays wherever you are. Confers no benefit. June 2025 Subscriber Item.",
"shieldMystery202508Text": "Brilliant Cyan Blade",
"shieldMystery202508Notes": "If you thought one spinning blade was cool looking, try two! Confers no benefit. August 2025 Subscriber Item.",
"shieldMystery301405Text": "Clock Shield", "shieldMystery301405Text": "Clock Shield",
"shieldMystery301405Notes": "Time is on your side with this towering clock shield! Confers no benefit. June 3015 Subscriber Item.", "shieldMystery301405Notes": "Time is on your side with this towering clock shield! Confers no benefit. June 3015 Subscriber Item.",
@@ -3174,6 +3188,8 @@
"shieldArmoireSpringPetalUchiwaNotes": "This handheld fan featuring a beautiful petal pattern creates a small breeze just for you as the weather warms. Increases Intelligence and Perception by <%= attrs %> each. Enchanted Armoire: Spring Petal Set (Item 2 of 2).", "shieldArmoireSpringPetalUchiwaNotes": "This handheld fan featuring a beautiful petal pattern creates a small breeze just for you as the weather warms. Increases Intelligence and Perception by <%= attrs %> each. Enchanted Armoire: Spring Petal Set (Item 2 of 2).",
"shieldArmoireBeekeepersHiveText": "Beehive", "shieldArmoireBeekeepersHiveText": "Beehive",
"shieldArmoireBeekeepersHiveNotes": "Beehives serve as both homes and worksites. You might need to talk to your bees about a good work-life balance. Increases Strength by <%= str %>. Enchanted Armoire: Beekeeper Set (Item 4 of 4)", "shieldArmoireBeekeepersHiveNotes": "Beehives serve as both homes and worksites. You might need to talk to your bees about a good work-life balance. Increases Strength by <%= str %>. Enchanted Armoire: Beekeeper Set (Item 4 of 4)",
"shieldArmoireFlyFishingRodText": "Fly Fishing Rod",
"shieldArmoireFlyFishingRodNotes": "Put a lure on this long and flexible rod and fish will mistake it for an insect every single time. Increases Strength and Intelligence by <%= attrs %> each. Enchanted Armoire: Fly Fishing Set (Item 3 of 3)",
"back": "Back Accessory", "back": "Back Accessory",
"backBase0Text": "No Back Accessory", "backBase0Text": "No Back Accessory",
@@ -3260,6 +3276,8 @@
"backMystery202505Notes": "Earn your stripes swooping and soaring on these aerodynamic wings. Confers no benefit. May 2025 Subscriber Item.", "backMystery202505Notes": "Earn your stripes swooping and soaring on these aerodynamic wings. Confers no benefit. May 2025 Subscriber Item.",
"backMystery202506Text": "Solar Shine Aureole", "backMystery202506Text": "Solar Shine Aureole",
"backMystery202506Notes": "Bring a warm glow with you as you go about your daily tasks. Confers no benefit. June 2025 Subscriber Item.", "backMystery202506Notes": "Bring a warm glow with you as you go about your daily tasks. Confers no benefit. June 2025 Subscriber Item.",
"backMystery202507Text": "Spunky Skateboard",
"backMystery202507Notes": "Your steed for the sidewalks and halfpipes. Confers no benefit. July 2025 Subscriber Item.",
"backSpecialWonderconRedText": "Mighty Cape", "backSpecialWonderconRedText": "Mighty Cape",
"backSpecialWonderconRedNotes": "Swishes with strength and beauty. Confers no benefit. Special Edition Convention Item.", "backSpecialWonderconRedNotes": "Swishes with strength and beauty. Confers no benefit. Special Edition Convention Item.",

View File

@@ -87,13 +87,12 @@
"API": "API", "API": "API",
"APICopied": "API token copied to clipboard.", "APICopied": "API token copied to clipboard.",
"APITokenTitle": "API Token", "APITokenTitle": "API Token",
"APITokenDisclaimer": "<b>Your API Token is like a password; Do not share it publicly.</b> You may occasionally be asked for your User ID, but never post your API Token where others can see it, including on Github.<br><br><b>Note:</b> If you need a new API Token (e.g., if you accidentally shared it), email <a href='mailto:admin@habitica.com' target='_blank'>admin@habitica.com</a> with your User ID and current Token. Once it is reset you will need to re-authorize everything by logging out of the website and mobile app and by providing the new Token to any other Habitica tools that you use.", "APITokenDisclaimer": "<b>Your API Token is like a password; Do not share it publicly.</b> You may occasionally be asked for your User ID, but never post your API Token where others can see it, including on Github.<br><br><b>If you need a new API Token</b> (e.g., if you accidentally shared it), you can change your password to reset it. Once it is reset, you will need to log back in to any other devices you use Habitica on and provide the new API Token to third-party tools you may use.",
"APIv3": "API v3", "APIv3": "API v3",
"APIText": "Copy these for use in third party applications. However, think of your API Token like a password, and do not share it publicly. You may occasionally be asked for your User ID, but never post your API Token where others can see it, including on Github.", "APIText": "Copy these for use in third party applications. However, think of your API Token like a password, and do not share it publicly. You may occasionally be asked for your User ID, but never post your API Token where others can see it, including on Github.",
"APIToken": "API Token (this is a password - see warning above!)", "APIToken": "API Token (this is a password - see warning above!)",
"showAPIToken": "Show API Token", "showAPIToken": "Show API Token",
"hideAPIToken": "Hide API Token", "hideAPIToken": "Hide API Token",
"APITokenWarning": "If you need a new API Token (e.g., if you accidentally shared it), email <%= hrefTechAssistanceEmail %> with your User ID and current Token. Once it is reset you will need to re-authorize everything by logging out of the website and mobile app and by providing the new Token to any other Habitica tools that you use.",
"thirdPartyApps": "Third Party Apps", "thirdPartyApps": "Third Party Apps",
"thirdPartyTools": "Find third party apps, extensions, and all kinds of other tools you can use with your account on the <a href='https://habitica.fandom.com/wiki/Extensions,_Add-Ons,_and_Customizations' target='_blank'>Habitica wiki</a>.", "thirdPartyTools": "Find third party apps, extensions, and all kinds of other tools you can use with your account on the <a href='https://habitica.fandom.com/wiki/Extensions,_Add-Ons,_and_Customizations' target='_blank'>Habitica wiki</a>.",
"resetDo": "Do it, reset my account!", "resetDo": "Do it, reset my account!",
@@ -212,7 +211,7 @@
"changeUsernameDisclaimer": "Your username is used for invitations, @mentions in chat, and messaging. It must be 1 to 20 characters, containing only letters a to z, numbers 0 to 9, hyphens, or underscores, and cannot include any inappropriate terms.", "changeUsernameDisclaimer": "Your username is used for invitations, @mentions in chat, and messaging. It must be 1 to 20 characters, containing only letters a to z, numbers 0 to 9, hyphens, or underscores, and cannot include any inappropriate terms.",
"changeEmailDisclaimer": "This is the email address that you use to log in to Habitica, as well as receive notifications.", "changeEmailDisclaimer": "This is the email address that you use to log in to Habitica, as well as receive notifications.",
"changeDisplayNameDisclaimer": "This is the name that will be displayed for your Avatar in Habitica.", "changeDisplayNameDisclaimer": "This is the name that will be displayed for your Avatar in Habitica.",
"changePasswordDisclaimer": "Password must be 8 characters or more. We recommend a strong password that you're not using elsewhere.", "changePasswordDisclaimer": "Passwords must be 8 characters or more. Changing your password will log you out of any other devices and third-party tools you may use.",
"dateFormatDisclaimer": "Adjust the date formatting across Habitica.", "dateFormatDisclaimer": "Adjust the date formatting across Habitica.",
"verifyUsernameVeteranPet": "One of these Veteran Pets will be waiting for you after you've finished confirming!", "verifyUsernameVeteranPet": "One of these Veteran Pets will be waiting for you after you've finished confirming!",
"enableAudio": "Enable Audio", "enableAudio": "Enable Audio",

View File

@@ -32,7 +32,7 @@
"subGemName": "Subscriber Gems", "subGemName": "Subscriber Gems",
"maxBuyGems": "You have bought all the Gems you can this month. More become available within the first three days of each month. Thanks for subscribing!", "maxBuyGems": "You have bought all the Gems you can this month. More become available within the first three days of each month. Thanks for subscribing!",
"timeTravelers": "Time Travelers", "timeTravelers": "Time Travelers",
"timeTravelersPopoverNoSubMobile": "Looks like youll need a Mystic Hourglass to open the time portal and summon the Mysterious Time Travelers.", "timeTravelersPopoverNoSubMobile": "Subscribers receive a rare Mystic Hourglass every month to use in the Time Travelers Shop!",
"timeTravelersPopover": "Your Mystic Hourglass has opened our time portal! Choose what youd like us to fetch from the past or future.", "timeTravelersPopover": "Your Mystic Hourglass has opened our time portal! Choose what youd like us to fetch from the past or future.",
"mysticHourglassNeededNoSub": "This item requires a Mystic Hourglass. You earn Mystic Hourglasses by being a Habitica subscriber.", "mysticHourglassNeededNoSub": "This item requires a Mystic Hourglass. You earn Mystic Hourglasses by being a Habitica subscriber.",
"mysterySetNotFound": "Mystery set not found, or set already owned.", "mysterySetNotFound": "Mystery set not found, or set already owned.",
@@ -175,6 +175,8 @@
"mysterySet202504": "Elusive Yeti Set", "mysterySet202504": "Elusive Yeti Set",
"mysterySet202505": "Soaring Swallowtail Set", "mysterySet202505": "Soaring Swallowtail Set",
"mysterySet202506": "Solar Shine Set", "mysterySet202506": "Solar Shine Set",
"mysterySet202507": "Spunky Skater Set",
"mysterySet202508": "Brilliant Blade Set",
"mysterySet301404": "Steampunk Standard Set", "mysterySet301404": "Steampunk Standard Set",
"mysterySet301405": "Steampunk Accessories Set", "mysterySet301405": "Steampunk Accessories Set",
"mysterySet301703": "Peacock Steampunk Set", "mysterySet301703": "Peacock Steampunk Set",

View File

@@ -906,5 +906,8 @@
"backgroundSummerSeashoreNotes": "在夏日海滨乘风破浪.", "backgroundSummerSeashoreNotes": "在夏日海滨乘风破浪.",
"backgrounds052025": "第 132 组于2025 年 5 月发布", "backgrounds052025": "第 132 组于2025 年 5 月发布",
"backgroundTrailThroughAForestText": "穿越森林的小径", "backgroundTrailThroughAForestText": "穿越森林的小径",
"backgroundTrailThroughAForestNotes": "沿着穿过森林的小径漫步。" "backgroundTrailThroughAForestNotes": "沿着穿过森林的小径漫步。",
"backgrounds072025": "SET 134: Released July 2025",
"backgroundSirensLairText": "Siren's Lair",
"backgroundSirensLairNotes": "Dare to dive into a Sirens Lair."
} }

View File

@@ -240,5 +240,8 @@
"sunsetFaqHeader12": "What will happen to Guild Bank Gems?", "sunsetFaqHeader12": "What will happen to Guild Bank Gems?",
"sunsetFaqPara21": "Gems in the Guild Bank will be refunded to the leader of the Guild on August 8th when Guild Services end.", "sunsetFaqPara21": "Gems in the Guild Bank will be refunded to the leader of the Guild on August 8th when Guild Services end.",
"anotherQuestion": "Have another question?", "anotherQuestion": "Have another question?",
"contactAdmin": "Contact <a href='mailto:admin@habitica.com'>admin@habitica.com</a>" "contactAdmin": "Contact <a href='mailto:admin@habitica.com'>admin@habitica.com</a>",
"webFaqAnswer60": "Here are some quick tips to get you started with your new Habitica Group Plan:\n\n * Promote a member to a manager to give them the ability to create and edit tasks\n * Leave tasks unassigned if anyone can complete it, and it only needs to be done once\n * Assign a task to one person to make sure no one else can complete their task\n * Assign a task to multiple people if they all need to complete it\n * Toggle the ability to display shared tasks on your personal board to not miss anything\n * You get rewarded for the tasks you complete, even multi-assigned\n * Task completion rewards arent split between members\n * Use task colour on the team board to judge the average completion rate of tasks\n * Regularly review the tasks on the shared task board to make sure they are still relevant\n * Missing a Daily wont damage you or your team, but the task will degrade in colour",
"webFaqAnswer67": "Classes are different roles that your character can play. Each class provides its own set of unique benefits and skills as you level up. These skills can complement the way you interact with your tasks or help you contribute to completing Quests in your Party.\n\nYour class also determines the Equipment that will be available to you for purchase in your Rewards, the Market, and the Seasonal Shop.\n\nHeres a rundown of each class to help you choose which one is best suited to your playstyle:\n#### **Warrior**\n* Warriors deal high damage to bosses and have a high chance of critical hits when completing tasks, rewarding you extra Experience and Gold.\n* Strength is their primary stat, raising the damage they do.\n* Constitution is their secondary stat, lowering the damage they take.\n* Warriors' skills buff their Party mates' Constitution and Strength.\n* Consider playing as a Warrior if you love to fight bosses but also want some protection if you miss tasks occasionally.\n#### **Healer**\n* Healers have high defence and can heal themselves as well as their Party mates.\n* Constitution is their primary stat, increasing their heals and lowering the damage they take.\n* Intelligence is their secondary stat, increasing their Mana and Experience.\n* Healers' skills make their tasks less red and buff their Party mates' Constitution.\n* Consider playing as a Healer if you miss tasks often and need the ability to heal yourself or your Party members. Healers also level up quickly.\n#### **Mage**\n* Mages level up quickly, gain lots of Mana, and damage bosses in Quests.\n* Intelligence is their primary stat, increasing their Mana and Experience.\n* Perception is their secondary stat, increasing their Gold and item drops.\n* Mages' skills freeze their task streaks, restore their Party mates' Mana, and buff their Intelligence.\n* Consider playing as a Mage if you are motivated by progressing quickly through levels and contributing damage to boss Quests.\n#### **Rogue**\n* Rogues get the most item drops and Gold from completing tasks, and have a high chance of critical hits, getting even more Experience and Gold.\n* Perception is their primary stat, increasing their Gold and item drops.\n* Strength is their secondary stat, raising the damage they do.\n* Rogues' skills help them dodge missed Dailies, pilfer Gold, and buff their Party mates Perception.\n* Consider playing as a Rogue if youre highly motivated by rewards.",
"sunsetFaqPara2": "Habiticas primary purpose is and has always been to provide a gamified task management experience. Taverns and Guilds helped motivate players by helping them find others with similar goals. Some truly wonderful spaces were created and we had a chance to see the community thrive with helpful discussion. As the years passed, we noticed changes in how players use and rely on Habitica. Parties flourished, while Guilds and public spaces were used by less and less of our player base. In an ever changing internet landscape, the resources necessary to maintain these spaces became too disproportionate to the number of people actually participating in them."
} }

View File

@@ -33,11 +33,11 @@
"marketing1Lead3Title": "Get rewarded for your effort", "marketing1Lead3Title": "Get rewarded for your effort",
"marketing1Lead3": "Having something to look forward to can be the difference between getting a task done, or having it taunt you for weeks. When life doesn't offer a reward, Habitica has you covered! Youll be rewarded for every task, but surprises are around every cornerso keep up your progress! ", "marketing1Lead3": "Having something to look forward to can be the difference between getting a task done, or having it taunt you for weeks. When life doesn't offer a reward, Habitica has you covered! Youll be rewarded for every task, but surprises are around every cornerso keep up your progress! ",
"marketing2Header": "Team up with friends", "marketing2Header": "Team up with friends",
"marketing2Lead1Title": "Social Productivity", "marketing2Lead1Title": "Social productivity",
"marketing2Lead1": "Get a boost of motivation by collaborating, competing, and interacting with others! Habitica is built to harness the most effective part of any self-improvement program: social accountability.", "marketing2Lead1": "Get a boost of motivation by collaborating, competing, and interacting with others! Habitica is built to harness the most effective part of any self-improvement program: social accountability.",
"marketing2Lead2Title": "Battle monsters on Quests", "marketing2Lead2Title": "Battle monsters on Quests",
"marketing2Lead2": "Take on one of our hundreds of Quests with a Party of friends to enter the fray. Quest monsters push your accountability to the max. Forgetting to floss means damage done to everyone!", "marketing2Lead2": "Take on one of our hundreds of Quests with a Party of friends to enter the fray. Quest monsters push your accountability to the max. Forgetting to floss means damage done to everyone!",
"marketing2Lead3Title": "Challenge Each Other", "marketing2Lead3Title": "Challenge each other",
"marketing2Lead3": "Join Challenges made by our community to get curated task lists that fit your interests and goals. Do your best to compete for the Gem prize awarded to the winner!", "marketing2Lead3": "Join Challenges made by our community to get curated task lists that fit your interests and goals. Do your best to compete for the Gem prize awarded to the winner!",
"marketing3Header": "Android & iOS Apps", "marketing3Header": "Android & iOS Apps",
"marketing3Lead1": "You can get Habitica on your Android or iOS device to check off tasks anywhere. Check out our award winning apps for a fresh approach to getting things done.", "marketing3Lead1": "You can get Habitica on your Android or iOS device to check off tasks anywhere. Check out our award winning apps for a fresh approach to getting things done.",
@@ -45,7 +45,7 @@
"marketing3Lead2": "We're proud to be an open-source project that welcomes contributions from our dedicated community. Make Habitica fit your own needs or contribute to improve the experience of all players around the world. Visit us on [GitHub](https://github.com/habitrpg) to learn more!", "marketing3Lead2": "We're proud to be an open-source project that welcomes contributions from our dedicated community. Make Habitica fit your own needs or contribute to improve the experience of all players around the world. Visit us on [GitHub](https://github.com/habitrpg) to learn more!",
"marketing4Header": "Beyond household chores", "marketing4Header": "Beyond household chores",
"marketing4Lead1": "Education is one of the best places for a little gamification! Break through the monotony of everyday classwork by adding some game play into the mix. Habitica can be a fun way to track homework, make classroom Challenges, and let your students show off their accomplishments.", "marketing4Lead1": "Education is one of the best places for a little gamification! Break through the monotony of everyday classwork by adding some game play into the mix. Habitica can be a fun way to track homework, make classroom Challenges, and let your students show off their accomplishments.",
"marketing4Lead1Title": "Gamification In Education", "marketing4Lead1Title": "Gamification in education",
"marketing4Lead2": "Building a healthier lifestyle can easily become an overwhelming endeavor. Habitica helps you track all aspects of your fitness goals with flexible scheduling and intensity to meet you where you are. So have some fun while you work your way towards better health!", "marketing4Lead2": "Building a healthier lifestyle can easily become an overwhelming endeavor. Habitica helps you track all aspects of your fitness goals with flexible scheduling and intensity to meet you where you are. So have some fun while you work your way towards better health!",
"marketing4Lead2Title": "Gamification In Health and Wellness", "marketing4Lead2Title": "Gamification In Health and Wellness",
"marketing4Lead3-1": "Ready to have fun getting things done?", "marketing4Lead3-1": "Ready to have fun getting things done?",
@@ -180,5 +180,8 @@
"emailUsernamePlaceholder": "e.g., habitrabbit or gryphon@example.com", "emailUsernamePlaceholder": "e.g., habitrabbit or gryphon@example.com",
"incorrectResetPhrase": "Please type <%= magicWord %> in all capital letters to reset your account.", "incorrectResetPhrase": "Please type <%= magicWord %> in all capital letters to reset your account.",
"enterHabitica": "Enter Habitica", "enterHabitica": "Enter Habitica",
"translateHabitica": "Translate Habitica" "translateHabitica": "Translate Habitica",
"marketing3Lead1Title": "Android & iOS apps",
"marketing4Lead3Button": "Get Started Today",
"missingClientHeader": "Missing x-client headers."
} }

View File

@@ -55,7 +55,6 @@
"APIToken": "API Token (this is a password—see warning above!)", "APIToken": "API Token (this is a password—see warning above!)",
"showAPIToken": "Show API Token", "showAPIToken": "Show API Token",
"hideAPIToken": "Hide API Token", "hideAPIToken": "Hide API Token",
"APITokenWarning": "If you need a new API Token (e.g., if you accidentally shared it), email <%= hrefTechAssistanceEmail %> with your User ID and current Token. Once it is reset you will need to re-authorise everything by logging out of the website and mobile app and by providing the new Token to any other Habitica tools that you use.",
"thirdPartyApps": "Third Party Apps", "thirdPartyApps": "Third Party Apps",
"resetDo": "Do it, reset my account!", "resetDo": "Do it, reset my account!",
"resetComplete": "Reset complete!", "resetComplete": "Reset complete!",
@@ -168,7 +167,7 @@
"contentRelease": "Content releases + Events", "contentRelease": "Content releases + Events",
"bannedWordUsedInProfile": "Your Display Name or About text contained inappropriate language.", "bannedWordUsedInProfile": "Your Display Name or About text contained inappropriate language.",
"changeDisplayNameDisclaimer": "This is the name that will be displayed for your Avatar in Habitica.", "changeDisplayNameDisclaimer": "This is the name that will be displayed for your Avatar in Habitica.",
"changePasswordDisclaimer": "Password must be 8 characters or more. We recommend a strong password that you're not using elsewhere.", "changePasswordDisclaimer": "Passwords must be 8 characters or more. Changing your password will log you out of any other devices and third party tools you may use.",
"dateFormatDisclaimer": "Adjust the date formatting across Habitica.", "dateFormatDisclaimer": "Adjust the date formatting across Habitica.",
"enableAudio": "Enable Audio", "enableAudio": "Enable Audio",
"playDemoAudio": "Play Demo", "playDemoAudio": "Play Demo",
@@ -196,7 +195,7 @@
"remove": "Remove", "remove": "Remove",
"resetTextLocal": "If you're absolutely certain, type your password into the text box below.", "resetTextLocal": "If you're absolutely certain, type your password into the text box below.",
"resetTextSocial": "If you're absolutely certain, type <b>\"<%= magicWord %>\"</b> into the text box below.", "resetTextSocial": "If you're absolutely certain, type <b>\"<%= magicWord %>\"</b> into the text box below.",
"APITokenDisclaimer": "<b>Your API Token is like a password; Do not share it publicly.</b> You may occasionally be asked for your User ID, but never post your API Token where others can see it, including on Github.<br><br><b>Note:</b> If you need a new API Token (e.g., if you accidentally shared it), email <a href='mailto:admin@habitica.com' target='_blank'>admin@habitica.com</a> with your User ID and current Token. Once it is reset you will need to re-authorise everything by logging out of the website and mobile app and by providing the new Token to any other Habitica tools that you use.", "APITokenDisclaimer": "<b>Your API Token is like a password; Do not share it publicly.</b> You may occasionally be asked for your User ID, but never post your API Token where others can see it, including on Github.<br><br><b>If you need a new API Token</b> (e.g., if you accidentally shared it), you can change your password to reset it. Once it is reset, you will need to log back in to any other devices you use Habitica on and provide the new API Token to third-party tools you may use.",
"audioThemeDisclaimer": "Audio themes add optional sound effects to the Habitica website. Volume levels are controlled using your computer's volume settings.", "audioThemeDisclaimer": "Audio themes add optional sound effects to the Habitica website. Volume levels are controlled using your computer's volume settings.",
"gemCap": "Gem Cap", "gemCap": "Gem Cap",
"nextHourglass": "Next Mystic Hourglass Delivery", "nextHourglass": "Next Mystic Hourglass Delivery",

View File

@@ -7,7 +7,7 @@
"reachedGoldToGemCapQuantity": "Your requested amount <%= quantity %> exceeds the amount you can buy for this month (<%= convCap %>). The full amount becomes available within the first three days of each month. Thanks for subscribing!", "reachedGoldToGemCapQuantity": "Your requested amount <%= quantity %> exceeds the amount you can buy for this month (<%= convCap %>). The full amount becomes available within the first three days of each month. Thanks for subscribing!",
"mysteryItem": "Exclusive monthly items", "mysteryItem": "Exclusive monthly items",
"mysteryItemText": "Each month you will receive a unique cosmetic item for your avatar! Plus, for every three months of consecutive subscription, the Mysterious Time Travelers will grant you access to historic (and futuristic!) cosmetic items.", "mysteryItemText": "Each month you will receive a unique cosmetic item for your avatar! Plus, for every three months of consecutive subscription, the Mysterious Time Travelers will grant you access to historic (and futuristic!) cosmetic items.",
"exclusiveJackalopePet": "Exclusive pet", "exclusiveJackalopePet": "Special Pet",
"giftSubscription": "Want to gift the benefits of a subscription to someone else?", "giftSubscription": "Want to gift the benefits of a subscription to someone else?",
"giftSubscriptionText4": "Thanks for supporting Habitica!", "giftSubscriptionText4": "Thanks for supporting Habitica!",
"groupPlans": "Group Plans", "groupPlans": "Group Plans",
@@ -21,7 +21,7 @@
"subGemName": "Subscriber Gems", "subGemName": "Subscriber Gems",
"maxBuyGems": "You have bought all the Gems you can this month. More become available within the first three days of each month. Thanks for subscribing!", "maxBuyGems": "You have bought all the Gems you can this month. More become available within the first three days of each month. Thanks for subscribing!",
"timeTravelers": "Time Travellers", "timeTravelers": "Time Travellers",
"timeTravelersPopoverNoSubMobile": "Looks like youll need a Mystic Hourglass to open the time portal and summon the Mysterious Time Travelers.", "timeTravelersPopoverNoSubMobile": "Subscribers receive a rare Mystic Hourglass every month to use in the Time Travellers Shop!",
"timeTravelersPopover": "Your Mystic Hourglass has opened our time portal! Choose what youd like us to fetch from the past or future.", "timeTravelersPopover": "Your Mystic Hourglass has opened our time portal! Choose what youd like us to fetch from the past or future.",
"mysterySetNotFound": "Mystery set not found, or set already owned.", "mysterySetNotFound": "Mystery set not found, or set already owned.",
"mysteryItemIsEmpty": "Mystery items are empty", "mysteryItemIsEmpty": "Mystery items are empty",
@@ -91,7 +91,7 @@
"mysterySet301703": "Peacock Steampunk Set", "mysterySet301703": "Peacock Steampunk Set",
"mysterySet301704": "Pheasant Steampunk Set", "mysterySet301704": "Pheasant Steampunk Set",
"mysterySetwondercon": "Wondercon", "mysterySetwondercon": "Wondercon",
"subUpdateCard": "Update Credit Card", "subUpdateCard": "Edit Credit Card",
"subUpdateTitle": "Update", "subUpdateTitle": "Update",
"notEnoughHourglasses": "You don't have enough Mystic Hourglasses.", "notEnoughHourglasses": "You don't have enough Mystic Hourglasses.",
"petsAlreadyOwned": "Pet already owned.", "petsAlreadyOwned": "Pet already owned.",
@@ -120,11 +120,11 @@
"gemBenefit2": "Backgrounds to immerse your avatar in the world of Habitica!", "gemBenefit2": "Backgrounds to immerse your avatar in the world of Habitica!",
"gemBenefit3": "Exciting Quest chains that drop pet eggs.", "gemBenefit3": "Exciting Quest chains that drop pet eggs.",
"gemBenefit4": "Reset your avatar's Stat Points and change its Class.", "gemBenefit4": "Reset your avatar's Stat Points and change its Class.",
"subscriptionBenefit1": "Alexander the Merchant will now sell you Gems from the Market for 20 Gold each!", "subscriptionBenefit1": "Get up to 50 Gold-purchasable Gems in the Market to buy Quests, Customizations, Pets, and more!",
"subscriptionBenefit3": "Discover even more items in Habitica with a 2x daily drop-cap.", "subscriptionBenefit3": "Find double the Eggs, Hatching Potions, and Food each day to grow your Pet collection!",
"subscriptionBenefit4": "Unique cosmetic item for you to decorate your avatar each month.", "subscriptionBenefit4": "Stay decked out in the latest exclusive gear. Subscribe now to get <%= month %>s <%= currentMysterySetName %>!",
"subscriptionBenefit5": "Receive the Royal Purple Jackalope pet when you become a new subscriber.", "subscriptionBenefit5": "Get the exclusive Royal Purple Jackalope when you subscribe today!",
"subscriptionBenefit6": "Earn Mystic Hourglasses to purchase items in the Time Travelers Shop!", "subscriptionBenefit6": "Never miss an item with 1 Mystic Hourglass a month to use in the Time Travellers Shop!",
"purchaseAll": "Purchase Set", "purchaseAll": "Purchase Set",
"gemsRemaining": "remaining", "gemsRemaining": "remaining",
"notEnoughGemsToBuy": "No more Gems available for purchase this month. More will become available within the first 3 days of each month.", "notEnoughGemsToBuy": "No more Gems available for purchase this month. More will become available within the first 3 days of each month.",
@@ -148,17 +148,17 @@
"viewSubscriptions": "View Subscriptions", "viewSubscriptions": "View Subscriptions",
"mysterySet202002": "Stylish Sweetheart Set", "mysterySet202002": "Stylish Sweetheart Set",
"cancelSubAlternatives": "If you're having technical problems or Habitica doesn't seem to be working out for you, please consider <a href='mailto:admin@habitica.com'>contacting us</a>. We want to help you get the most from Habitica.", "cancelSubAlternatives": "If you're having technical problems or Habitica doesn't seem to be working out for you, please consider <a href='mailto:admin@habitica.com'>contacting us</a>. We want to help you get the most from Habitica.",
"cancelYourSubscription": "Cancel your subscription?", "cancelYourSubscription": "Need to cancel your subscription?",
"readyToResubscribe": "Are you ready to resubscribe?", "readyToResubscribe": "Are you ready to resubscribe?",
"needToUpdateCard": "Need to update your card?", "needToUpdateCard": "Need to update your card?",
"subMonths": "Sub Months", "subMonths": "Months Subscribed",
"subscriptionStats": "Subscription Stats", "subscriptionStats": "Subscription Stats",
"subscriptionInactiveDate": "Your subscription benefits will become inactive on <br><strong><%= date %></strong>", "subscriptionInactiveDate": "Your subscription benefits will become inactive on <br><strong><%= date %></strong>",
"subscriptionCanceled": "Your subscription is canceled", "subscriptionCanceled": "Your subscription is canceled",
"youAreSubscribed": "You are subscribed to Habitica", "youAreSubscribed": "You are subscribed to Habitica",
"doubleDropCap": "Double the Drops", "doubleDropCap": "Double the Drops",
"monthlyMysteryItems": "Monthly Mystery Items", "monthlyMysteryItems": "Limited Monthly Gear Sets",
"subscribersReceiveBenefits": "Subscribers receive these useful benefits!", "subscribersReceiveBenefits": "Stay motivated with even more rewards when you subscribe",
"giftASubscription": "Gift a Subscription", "giftASubscription": "Gift a Subscription",
"mysterySet202003": "Barbed Battler Set", "mysterySet202003": "Barbed Battler Set",
"mysterySet202004": "Mighty Monarch Set", "mysterySet202004": "Mighty Monarch Set",
@@ -231,5 +231,6 @@
"mysterySet202305": "Eventide Dragon Set", "mysterySet202305": "Eventide Dragon Set",
"mysterySet202302": "Trickster Tabby Set", "mysterySet202302": "Trickster Tabby Set",
"mysterySet202304": "Tiptop Teapot Set", "mysterySet202304": "Tiptop Teapot Set",
"mysterySet202310": "Ghostlight Wraith Set" "mysterySet202310": "Ghostlight Wraith Set",
"monthlyMysticHourglass": "Monthly Mystic Hourglass"
} }

View File

@@ -156,7 +156,7 @@
"achievementBonelessBossModalText": "¡Tienes todas las mascotas invertebradas en tu colección!", "achievementBonelessBossModalText": "¡Tienes todas las mascotas invertebradas en tu colección!",
"achievementBonelessBossText": "Ha eclosionado todos los colores estándar de mascotas invertebradas: escarabajo, mariposa calamar, nudibranquio, pulpo, caracol y araña!", "achievementBonelessBossText": "Ha eclosionado todos los colores estándar de mascotas invertebradas: escarabajo, mariposa calamar, nudibranquio, pulpo, caracol y araña!",
"achievementBonelessBoss": "Jefe deshuesado", "achievementBonelessBoss": "Jefe deshuesado",
"achievementDuneBuddyText": "Ha eclosionado todos los colores estándar de mascotas de clima desértico: armadillo, cactus, zorro, rana, serpiente y araña.", "achievementDuneBuddyText": "¡Ha eclosionado todos los colores estándar de mascotas de clima desértico: armadillo, cactus, zorro, rana, serpiente y araña!",
"achievementDuneBuddy": "Amigo de médano", "achievementDuneBuddy": "Amigo de médano",
"achievementDuneBuddyModalText": "¡Has conseguido todas las mascotas de desierto!", "achievementDuneBuddyModalText": "¡Has conseguido todas las mascotas de desierto!",
"achievementRoughRiderModalText": "¡Has conseguido todos los colores básicos de las mascotas y monturas incómodas!", "achievementRoughRiderModalText": "¡Has conseguido todos los colores básicos de las mascotas y monturas incómodas!",

View File

@@ -906,5 +906,11 @@
"backgroundTrailThroughAForestNotes": "Dembulando bajo en un sendero en el bosque.", "backgroundTrailThroughAForestNotes": "Dembulando bajo en un sendero en el bosque.",
"backgrounds062025": "Conjunto 133: Publicado en Junio 2025", "backgrounds062025": "Conjunto 133: Publicado en Junio 2025",
"backgroundSummerSeashoreText": "Verano en la Costa", "backgroundSummerSeashoreText": "Verano en la Costa",
"backgroundSummerSeashoreNotes": "Atrapa una ola este Verano en la Costa." "backgroundSummerSeashoreNotes": "Atrapa una ola este Verano en la Costa.",
"backgrounds072025": "Conjunto 134: Publicado en Julio 2025",
"backgroundSirensLairText": "Guarida de Sirena",
"backgroundSirensLairNotes": "Atrévete a sumergirte en la Guarida de Sirena.",
"backgroundSunnyStreetWithShopsText": "Calle Soleada con Tiendas",
"backgrounds082025": "Conjunto 135: Publicado en Agosto 2025",
"backgroundSunnyStreetWithShopsNotes": "Embriaga tus sentidos con las imágenes y los sonidos de esta Calle Soleada con Tiendas."
} }

View File

@@ -182,5 +182,7 @@
"translateHabitica": "Traduce Habitica", "translateHabitica": "Traduce Habitica",
"incorrectResetPhrase": "Por favor, teclea <%= magicWord %> en mayúsculas para reiniciar tu cuenta.", "incorrectResetPhrase": "Por favor, teclea <%= magicWord %> en mayúsculas para reiniciar tu cuenta.",
"marketing3Lead1Title": "Aplicaciones para Android y iOS", "marketing3Lead1Title": "Aplicaciones para Android y iOS",
"marketing4Lead3Button": "Empieza Hoy Mismo" "marketing4Lead3Button": "Empieza Hoy Mismo",
"missingClientHeader": "Faltan los encabezados x-client.",
"emailBlockedRegistration": "Esta cuenta de E-Mail está bloqueada desde el registro. Si crees que es un error, por favor contacta con nosotros por medio de admin@habitica.com."
} }

View File

@@ -3365,5 +3365,23 @@
"shieldSpecialSummer2025HealerText": "Escudo Ángel de Mar", "shieldSpecialSummer2025HealerText": "Escudo Ángel de Mar",
"shieldArmoireBeekeepersHiveText": "Panal", "shieldArmoireBeekeepersHiveText": "Panal",
"backMystery202506Text": "Aureola de Brillo Solar", "backMystery202506Text": "Aureola de Brillo Solar",
"backMystery202506Notes": "Derrocha un aura de calidez mientras completas tus tareas diarias. No otorga ningún beneficio. Artículo de Suscriptor Junio 2025." "backMystery202506Notes": "Derrocha un aura de calidez mientras completas tus tareas diarias. No otorga ningún beneficio. Artículo de Suscriptor Junio 2025.",
"armorArmoireFlyFishingWadersText": "Katiuskas para Pesca Fluvial",
"armorArmoireFlyFishingWadersNotes": "Te mantienen perfectamente calentito y seco mientras te dedicas a lo tuyo ya sea dentro de un arroyo, estanque, lago o río. Aumenta la fuerza y la constitución en <%= attrs %> cada uno. Armario Encantado: Conjunto Pescador Fluvial (Artículo 2 de 3)",
"headMystery202507Text": "Gorra de Patinador Audaz",
"headMystery202507Notes": "Llevar la gorra hacia atrás o ladeada es aún guay, ¿verdad tronco, fliiipas? No otorga ningún beneficio. Artículo de Suscriptor Julio 2025.",
"headArmoireFlyFishingHatText": "Gorro de Pescador Fluvial",
"headArmoireFlyFishingHatNotes": "Con un ala ancha y su espacio para colocar un señuelo de repuesto, no salgas a pescar con mosca sin esta hermosura. Aumenta la Fuerza y la Percepción en <%= attrs %> cada uno. Armario Encantado: Conjunto de Pescador Fluvial (Artículo 1 de 3)",
"shieldArmoireFlyFishingRodNotes": "Coloca un cebo en esta caña larga y flexible y engañará a los peces que lo confundirán con una auténtica mosca vez tras vez, garantizado. Aumenta la Fuerza y la Inteligencia en <%= attrs %> cada uno. Armario Encantado: Conjunto de Pescador Fluvial (Artículo 3 de 3)",
"shieldArmoireFlyFishingRodText": "Caña de Pescador Fluvial",
"backMystery202507Text": "Tabla del Audaz",
"backMystery202507Notes": "Tu montura para las aceras y los medio-tubos. No otorga ningún beneficio. Artículo de Suscriptor Julio 2025.",
"weaponMystery202508Text": "Cuchilla Brillante Carmesí",
"weaponMystery202508Notes": "¡Esta cuchilla giratoria será el terror de cualquier monstruo o cualquier Tarea Diaria en rojo que se cruce en tu camino, huirán despavoridos! No otorga ningún beneficio. Artículo de Suscriptor Agosto 2025.",
"headArmoireRedNewsieHatText": "Gorra Roja de Vendedor de Periódicos",
"headArmoireRedNewsieHatNotes": "¡Extra! ¡Extra! Leed las jugosas últimas noticias: esta gorra es cómoda, práctica y está a la moda. Aumenta la Percepción y la Inteligencia en <%= attrs %> cada uno. Armario Encantado: Conjunto Chaleco Rojo (Artículo 1 de 2)",
"shieldMystery202508Text": "Cuchilla Brillante Cian",
"shieldMystery202508Notes": "Si una cuchilla giratoria ya te había volado el cerebro ¡Imagínate dos! Un auténtico disparate no apto para todas las mentes. No otorga ningún beneficio. Artículo de Suscriptor Agosto 2025.",
"armorArmoireRedWaistcoatText": "Chaleco Rojo",
"armorArmoireRedWaistcoatNotes": "Viste elegante y con clase mientras cumples con tus tareas. Hay algo secreto escondido en el bolsillo interior, pero, quién no tiene uno o dos cadáveres escondidos en el armario—¿qué crees que será, precioso mío? Aumenta la Constitución y la Fuerza en <%= attrs %>. Armario Encantado: Conjunto Chaleco Rojo (Artículo 2 de 2)"
} }

View File

@@ -55,7 +55,6 @@
"APIToken": "API Token (esto es una contraseña - mira la advertencia de arriba!)", "APIToken": "API Token (esto es una contraseña - mira la advertencia de arriba!)",
"showAPIToken": "Enseñar la ficha API", "showAPIToken": "Enseñar la ficha API",
"hideAPIToken": "Esconder la ficha API", "hideAPIToken": "Esconder la ficha API",
"APITokenWarning": "Si necesitas un nuevo API Token (e.g. si accidentalmente has compartido el tuyo), envía un email a <%= hrefTechAssistanceEmail %> con tu Número de Usuario y API Token actual. Una vez sea restablecido tu nuevo API Token, tendrás que volver a autorizar cualquier dispositivo cerrando la sesión pasada en la web y la aplicación móvil e ingresando el nuevo Token en cualquier dispositivo en el que uses Habitica.",
"thirdPartyApps": "Aplicaciones de terceros", "thirdPartyApps": "Aplicaciones de terceros",
"resetDo": "¡Adelante! ¡Reinicia mi cuenta!", "resetDo": "¡Adelante! ¡Reinicia mi cuenta!",
"resetComplete": "¡Reseteo completado!", "resetComplete": "¡Reseteo completado!",
@@ -215,7 +214,7 @@
"showHatchPetModal": "Al eclosionar una mascota", "showHatchPetModal": "Al eclosionar una mascota",
"baileyAnnouncement": "Último anuncio de Bailey", "baileyAnnouncement": "Último anuncio de Bailey",
"view": "Ver", "view": "Ver",
"APITokenDisclaimer": "<b>Tu token de API Token es como una contraseña; no lo compartas.</b> Es posible que se te pida tu ID de usuario, pero nunca publiques tu token de API donde otras personas puedan verlo, incluido Github.<br><br><b>Nota:</b> Si necesitas un nuevo token de API (p. ej., si lo has compartido por error), envía un correo electrónico a <a href='mailto:admin@habitica.com' target='_blank'>admin@habitica.com</a> con tu ID de usuario y tu token actual. Una vez se haya restaurado, necesitarás volver a autorizarlo todo cerrando sesión de la versión web y móvil y añadiendo el nuevo token a todas las herramientas de Habitica que uses.", "APITokenDisclaimer": "<b>Tu token de API Token es como una contraseña; no lo compartas.</b> Es posible que se te pida tu ID de usuario, pero nunca publiques tu token de API donde otras personas puedan verlo, incluido Github.<br><br><b>Si necesitas un nuevo token de API </b> (p. ej., si lo has compartido por error), puedes simplemente cambiar tu contraseña para obtener uno nuevo. Una vez se haya restaurado, necesitarás volver a acceder de nuevo en todos los dispositivos en los que estés usando Habitica y añadir el nuevo token de API a todas las herramientas de terceros que uses.",
"thirdPartyTools": "Encuentra aplicaciones de terceros, extensiones y todo tipo de herramientas que puedes utilizar con tu cuenta en la <a href='https://habitica.fandom.com/wiki/Extensions,_Add-Ons,_and_Customizations' target='_blank'>wiki de Habitica</a>.", "thirdPartyTools": "Encuentra aplicaciones de terceros, extensiones y todo tipo de herramientas que puedes utilizar con tu cuenta en la <a href='https://habitica.fandom.com/wiki/Extensions,_Add-Ons,_and_Customizations' target='_blank'>wiki de Habitica</a>.",
"transaction_create_bank_challenge": "Desafío de banco <b>creado</b>", "transaction_create_bank_challenge": "Desafío de banco <b>creado</b>",
"showRaisePetModal": "Al transformar una mascota en montura", "showRaisePetModal": "Al transformar una mascota en montura",
@@ -242,7 +241,7 @@
"addWebhook": "Añadir Webhook", "addWebhook": "Añadir Webhook",
"changeEmailDisclaimer": "Este es el correo electrónico que usarás para iniciar sesión en Habitica y en el que recibirás notificaciones.", "changeEmailDisclaimer": "Este es el correo electrónico que usarás para iniciar sesión en Habitica y en el que recibirás notificaciones.",
"changeDisplayNameDisclaimer": "Este es el nombre que aparecerá junto a tu avatar en Habitica.", "changeDisplayNameDisclaimer": "Este es el nombre que aparecerá junto a tu avatar en Habitica.",
"changePasswordDisclaimer": "La contraseña debe tener al menos 8 caracteres. Recomendamos usar una contraseña fuerte que no estés usando en ningún otro servicio.", "changePasswordDisclaimer": "La contraseña debe tener al menos 8 caracteres. Al cambiar tu contraseña tendrás que volver a acceder en otros dispositivos y en herramientas de terceros que estés usando.",
"dateFormatDisclaimer": "Ajustar el formato de fecha de Habitica.", "dateFormatDisclaimer": "Ajustar el formato de fecha de Habitica.",
"enableAudio": "Activar audio", "enableAudio": "Activar audio",
"playDemoAudio": "Reproducir demostración", "playDemoAudio": "Reproducir demostración",

View File

@@ -21,7 +21,7 @@
"subGemName": "Gemas de suscriptor", "subGemName": "Gemas de suscriptor",
"maxBuyGems": "Has comprado todas las Gemas que podías comprar este mes. Habrá más gemas disponibles a partir de los tres primeros días de cada mes. ¡Gracias por suscribirte!", "maxBuyGems": "Has comprado todas las Gemas que podías comprar este mes. Habrá más gemas disponibles a partir de los tres primeros días de cada mes. ¡Gracias por suscribirte!",
"timeTravelers": "Viajeros del tiempo", "timeTravelers": "Viajeros del tiempo",
"timeTravelersPopoverNoSubMobile": "Parece que necesitarás un Reloj de Arena Místico para abrir el portal del tiempo y convocar a los Misteriosos Viajeros del Tiempo.", "timeTravelersPopoverNoSubMobile": "¡Los suscriptores reciben uno de los raros Relojes de arena Místico cada mes para usarlo en la Tienda de los Viajeros del Tiempo!",
"timeTravelersPopover": "¡Tu Reloj de Arena Místico ha abierto nuestro portal del tiempo! Elige lo que deseas que obtengamos del pasado o futuro.", "timeTravelersPopover": "¡Tu Reloj de Arena Místico ha abierto nuestro portal del tiempo! Elige lo que deseas que obtengamos del pasado o futuro.",
"mysterySetNotFound": "El conjunto misterioso no se ha encontrado, o bien ya lo tienes.", "mysterySetNotFound": "El conjunto misterioso no se ha encontrado, o bien ya lo tienes.",
"mysteryItemIsEmpty": "Artículos misteriosos están vacíos", "mysteryItemIsEmpty": "Artículos misteriosos están vacíos",
@@ -264,5 +264,7 @@
"mysterySet202503": "Conjunto de Juggernaut Jade", "mysterySet202503": "Conjunto de Juggernaut Jade",
"mysterySet202504": "Conjunto de Yeti Esquivo", "mysterySet202504": "Conjunto de Yeti Esquivo",
"mysterySet202505": "Conjunto Mariposa Macaón Masiva", "mysterySet202505": "Conjunto Mariposa Macaón Masiva",
"mysterySet202506": "Conjunto Brillo Solar" "mysterySet202506": "Conjunto Brillo Solar",
"mysterySet202507": "Conjunto de Patinador Audaz",
"mysterySet202508": "Conjunto Cuchilla Brillante"
} }

View File

@@ -783,5 +783,9 @@
"backgroundInAnAquariumText": "En el acuario", "backgroundInAnAquariumText": "En el acuario",
"backgroundInAnAquariumNotes": "Date un baño tranquilo con los peces En el acuario.", "backgroundInAnAquariumNotes": "Date un baño tranquilo con los peces En el acuario.",
"backgroundInsideAdventurersHideoutText": "En la guarida de un aventurero", "backgroundInsideAdventurersHideoutText": "En la guarida de un aventurero",
"backgroundInsideAdventurersHideoutNotes": "Planea tu travesía en la Guarida de un Aventurero." "backgroundInsideAdventurersHideoutNotes": "Planea tu travesía en la Guarida de un Aventurero.",
"backgrounds042023": "CONJUNTO 107: Publicado en abril de 2023",
"backgroundCraterLakeText": "Lago del Cráter",
"backgroundCraterLakeNotes": "Admira un Lago del Cráter hermoso.",
"backgrounds072023": "CONJUNTO 110: Publicado en Julio de 2023"
} }

View File

@@ -55,7 +55,6 @@
"APIToken": "Ficha API (ésta es una contraseña - ¡lee el aviso de arriba!)", "APIToken": "Ficha API (ésta es una contraseña - ¡lee el aviso de arriba!)",
"showAPIToken": "Mostrar ficha API", "showAPIToken": "Mostrar ficha API",
"hideAPIToken": "Ocultar ficha API", "hideAPIToken": "Ocultar ficha API",
"APITokenWarning": "Si necesitas una nueva Ficha API (ej. si accidentalmente la compartiste) Manda un email a <%= hrefTechAssistanceEmail %> con tu ID de usuario y tu Ficha actual. Una vez que se cambie la Ficha tendrás que re-autorizar todo saliendo de tu cuenta en la pagina y en la aplicación móvil, y dando una nueva Ficha a cualquier otra herramienta de Habitica que uses.",
"thirdPartyApps": "Aplicaciones de terceros", "thirdPartyApps": "Aplicaciones de terceros",
"resetDo": "¡Adelante, reinicia mi cuenta!", "resetDo": "¡Adelante, reinicia mi cuenta!",
"resetComplete": "Reseteo completo!", "resetComplete": "Reseteo completo!",

View File

@@ -906,5 +906,11 @@
"backgroundTrailThroughAForestNotes": "Randonnez sur les Chemins d'une Forêt.", "backgroundTrailThroughAForestNotes": "Randonnez sur les Chemins d'une Forêt.",
"backgrounds062025": "Ensemble 133 : Sortie en Juin 2025", "backgrounds062025": "Ensemble 133 : Sortie en Juin 2025",
"backgroundSummerSeashoreText": "Bord de Mer Estival", "backgroundSummerSeashoreText": "Bord de Mer Estival",
"backgroundSummerSeashoreNotes": "Faites des vagues sur ce Bord de Mer Estival." "backgroundSummerSeashoreNotes": "Faites des vagues sur ce Bord de Mer Estival.",
"backgrounds072025": "Ensemble 133 : Sortie Juillet 2025",
"backgroundSirensLairText": "Antre des Sirènes",
"backgroundSirensLairNotes": "Osez nager au cœur de l'Antre des Sirènes.",
"backgrounds082025": "Ensemble 135 : Sortie Août 2025",
"backgroundSunnyStreetWithShopsText": "Allée Marchande Ensoleillée",
"backgroundSunnyStreetWithShopsNotes": "Profitez de l'ambiance lumineuse et sonore d'une Allée Marchande Ensoleillée."
} }

View File

@@ -31,26 +31,26 @@
"marketing1Lead2Title": "Équipez-vous avec classe", "marketing1Lead2Title": "Équipez-vous avec classe",
"marketing1Lead2": "Collectez des armes, des armures et bien plus encore avec l'Or que vous obtenez en accomplissant vos tâches. Avec des centaines d'objets à collectionner et à essayer, vous ne serez jamais à court de combinaisons à tester. Optimisez vos choix pour les stats, le style, ou les deux ! ", "marketing1Lead2": "Collectez des armes, des armures et bien plus encore avec l'Or que vous obtenez en accomplissant vos tâches. Avec des centaines d'objets à collectionner et à essayer, vous ne serez jamais à court de combinaisons à tester. Optimisez vos choix pour les stats, le style, ou les deux ! ",
"marketing1Lead3Title": "Obtenez des récompenses pour vos efforts", "marketing1Lead3Title": "Obtenez des récompenses pour vos efforts",
"marketing1Lead3": "Certains trouvent leur motivation dans le jeu et le hasard : un système appelé \"récompense stochastique\". Habitica accueille touts les styles de consolidations et de punitions : positives, négatives, prévisibles et aléatoires.", "marketing1Lead3": "Avoir un objectif clair et une récompense à la clef peut faire toute la différence pour accomplir une tâche, plutôt que de la garder en tête pendant des semaines. Quand la vie ne vous fait pas de cadeaux, Habitica s'en occupe ! Vous serez récompensé·e pour chaque tâche accomplie, et d'autres surprises pourraient apparaître de temps à autres, alors ne relâchez pas vos efforts ! ",
"marketing2Header": "Soyez en compétition avec vos amis", "marketing2Header": "Faites équipe avec des ami·e·s",
"marketing2Lead1Title": "Productivité sociale", "marketing2Lead1Title": "Productivité sociale",
"marketing2Lead1": "Même si vous pouvez jouer en solo à Habitica, tout devient réellement intéressant dès lors que vous commencez à vous associer, à entrer en compétition et à rendre des comptes. La partie la plus efficace de n'importe quel programme d'amélioration personnelle est la responsabilité sociale, et quel meilleur environnement pour la responsabilité et la compétition qu'un jeu vidéo ?", "marketing2Lead1": "Obtenez un surplus de motivation en collaborant, interagissant et/ou en vous mesurant à d'autres personnes ! Habitica a été conçu pour utiliser une partie essentielle du développement personnel : la dynamique de groupe.",
"marketing2Lead2Title": "Combattez des monstres", "marketing2Lead2Title": "Mesurez-vous à des monstres via des Quêtes",
"marketing2Lead2": "Qu'est-ce qu'un jeu de rôle sans batailles ? Combattez des monstres avec votre équipe. Les monstres vous mettent en mode \"super responsable\" : un jour où vous loupez la gym, c'est un jour où le monstre attaque *tout le monde !*", "marketing2Lead2": "Participez à des centaines de Quêtes avec une Équipe d'ami·e·s pour vous jete dans la bataille. Les monstres de Quêtes vous encouragent toujours plus à effectuer vos tâches. Si vous oubliez de faire votre lit, et toute votre équipe prendra des dégâts !",
"marketing2Lead3Title": "Défiez-vous les uns les autres", "marketing2Lead3Title": "Défiez-vous les un·e·s les autres",
"marketing2Lead3": "Les défis vous permettent d'être en compétition avec vos amis ou des inconnus. Celui ou celle qui a fait de son mieux à la fin du défi remporté des prix spéciaux.", "marketing2Lead3": "Rejoignez des Défis créés par notre communauté pour accomplir des tâches pré-définies qui correspondent à vos intérêts et vos objectifs. Faites de votre mieux pour obtenir les Gemmes qui seront obtenues par l·e·a gagnant·e !",
"marketing3Header": "Applications et extensions", "marketing3Header": "Plus de façons d'exploiter Habitica",
"marketing3Lead1": "Les applications iPhone et Android vous permettent de vous occuper de vos affaires en déplacement. Nous somme conscients que se connecter sur le site web pour cliquer sur des boutons peut être un frein.", "marketing3Lead1": "Vous pouvez utiliser Habitica sur votre appareil Android ou iOS pour valider vos tâches où que vous soyez. Consultez notre application primée pour aborder vos tâches sous un nouvel angle.",
"marketing3Lead2Title": "Intégrations", "marketing3Lead2Title": "Communauté Open-Source",
"marketing3Lead2": "D'autres **outils tiers** connectent Habitica à d'autres aspects de votre vie. Notre API permet lintégration facile de choses comme l'[extension Chrome](https://chrome.google.com/webstore/detail/habitica/pidkmpibnnnhneohdgjclfdjpijggmjj?hl=fr-FR), avec laquelle vous perdez des points en naviguant sur des sites non-productifs, et en gagnez sur des sites productifs. [Plus d'informations ici](https://habitica.fandom.com/fr/wiki/Extensions,_modules_compl%C3%A9mentaires_et_personnalisations).", "marketing3Lead2": "Nous sommes fier·ère·s de porter un projet open-source qui accueille les contributions de notre communauté dévouée. Adaptez Habitica à vos besoins ou participez à l'amélioration de l'expérience pour tou·te·s les joueur·euse·s à travers le monde. Rendez-vous sur [GitHub](https://github.com/HabitRPG/habitica/wiki/Contributing-to-Habitica) (site Anglais) pour en savoir plus !",
"marketing4Header": "Utilisation par une organisation", "marketing4Header": "Au-delà des tâches domestiques",
"marketing4Lead1": "L'éducation est l'un des meilleurs domaines pour la ludification. Nous savons tous à quel point les étudiants sont collés à leurs téléphones ; exploitez ce pouvoir ! Dressez vos élèves les uns contre les autres dans une compétition amicale. Récompensez les bons résultats avec des récompenses rares. Observez leurs notes et leur comportement monter en flèche.", "marketing4Lead1": "Lapprentissage devient bien plus motivant avec un soupçon de ludification ! Brisez la routine quotidienne de votre classe en ajoutant que une expérience de jeu unique. Habitica peut offrir une manière ludique de suivre les devoirs, de créer des Défis en classe et de permettre aux élèves de mettre en valeur leurs réussites.",
"marketing4Lead1Title": "La ludification dans l'éducation", "marketing4Lead1Title": "La ludification dans l'éducation",
"marketing4Lead2": "Les coûts liés à la santé augmentent et quelqu'un doit forcément les payer. Des centaines de programmes sont conçus pour réduire ces coûts et améliorer le bien-être général. Nous sommes convaincus qu'Habitica peut apporter une réelle solution vers un mode de vie plus sain.", "marketing4Lead2": "Adopter un mode de vie plus sain peut vite sembler décourageant. Habitica vous aide à suivre tous les aspects de vos objectifs fitness grâce à un planning flexible et une intensité adaptable à votre rythme. De quoi joindre lutile à lagréable sur le chemin du mieux-être !",
"marketing4Lead2Title": "La ludification dans la santé et le bien-être", "marketing4Lead2Title": "La ludification dans la santé et le bien-être",
"marketing4Lead3-1": "Vous voulez faire de votre vie un jeu ?", "marketing4Lead3-1": "Prêt·e à joindre lutile à lagréable ?",
"marketing4Lead3-2": "Intéressé par la gestion d'un groupe dans l'éducation, le bien-être, et davantage ?", "marketing4Lead3-2": "Intéressé par la gestion d'un groupe dans l'éducation, le bien-être, et davantage ?",
"marketing4Lead3Title": "Transformez tout en jeu", "marketing4Lead3Title": "Commencez votre voyage !",
"mobileAndroid": "Application Android", "mobileAndroid": "Application Android",
"mobileIOS": "Application iOS", "mobileIOS": "Application iOS",
"oldNews": "Nouvelles", "oldNews": "Nouvelles",
@@ -180,5 +180,8 @@
"emailUsernamePlaceholder": "par exemple habitrabbit ou gryphon@example.com", "emailUsernamePlaceholder": "par exemple habitrabbit ou gryphon@example.com",
"footerProduct": "Produit", "footerProduct": "Produit",
"translateHabitica": "Traduire Habitica", "translateHabitica": "Traduire Habitica",
"incorrectResetPhrase": "Merci de taper <%= magicWord %> en lettres capitales pour réinitialiser votre compte." "incorrectResetPhrase": "Merci de taper <%= magicWord %> en lettres capitales pour réinitialiser votre compte.",
"marketing3Lead1Title": "Applications Android et iOS",
"marketing4Lead3Button": "Commencez dès Aujourd'hui",
"missingClientHeader": "En-têtes x-client manquants."
} }

View File

@@ -3365,5 +3365,23 @@
"shieldArmoireBeekeepersHiveNotes": "Les ruches servent à la fois de domicile et de lieu de travail. Il faudrait peut-être discuter d'un bon équilibre entre vie professionnelle et vie privée avec vos abeilles. Augmente la Force de <%= str %>. Armoire Enchantée : Ensemble d'Apicult·eur·rice (Objet 4 sur 4)", "shieldArmoireBeekeepersHiveNotes": "Les ruches servent à la fois de domicile et de lieu de travail. Il faudrait peut-être discuter d'un bon équilibre entre vie professionnelle et vie privée avec vos abeilles. Augmente la Force de <%= str %>. Armoire Enchantée : Ensemble d'Apicult·eur·rice (Objet 4 sur 4)",
"backMystery202506Text": "Auréole de Rayonnement Solaire", "backMystery202506Text": "Auréole de Rayonnement Solaire",
"backMystery202506Notes": "Apporte une douce chaleur à vos tâches quotidiennes. Ne confère aucun bonus. Équipement d'Abonnement Juin 2025.", "backMystery202506Notes": "Apporte une douce chaleur à vos tâches quotidiennes. Ne confère aucun bonus. Équipement d'Abonnement Juin 2025.",
"shieldSpecialSummer2025RogueNotes": "Ce tentacule sembles fébrile, mais ses ventouses ont une sacrée poigne. Ennemi·e·s, gare à vous ! Augmente la Force de <%= str %>. Équipement Édition Limitée Été 2025." "shieldSpecialSummer2025RogueNotes": "Ce tentacule sembles fébrile, mais ses ventouses ont une sacrée poigne. Ennemi·e·s, gare à vous ! Augmente la Force de <%= str %>. Équipement Édition Limitée Été 2025.",
"armorArmoireFlyFishingWadersNotes": "Restez au chaud et au sec en vous immergeant dans un ruisseau, une mare, un lac ou une rivière. Augmente la Force et la Constitution de <%= attrs %> chacune. Armoire Enchantée : Ensemble Pêche à la Mouche (Objet 2 sur 3)",
"headArmoireFlyFishingHatNotes": "Avec sa large visière et son espace prévu pour fixer un leurre supplémentaire, n'oubliez pas de l'emporter avec pour partir à la pêche à la mouche. Augmente la Force et la Perception de <%= attrs %> chacune. Armoire Enchantée : Ensemble Pêche à la Mouche (Objet 1 sur 3)",
"shieldArmoireFlyFishingRodNotes": "Metter un appât sur cette canne longue et flexible, et les poissons penseront à chaque fois que c'est un insecte. Augmente la Force et l'Intelligence de <%= attrs %> chacune. Armoire Enchantée : Ensemble Pêche à la Mouche (Objet 3 sur 3)",
"armorArmoireFlyFishingWadersText": "Cuissardes de Pêche à la Mouche",
"headMystery202507Text": "Casquette Skateu·r·se Stylé·e",
"headMystery202507Notes": "La casquette à l'envers est toujours à la mode, hein ? Ne confère aucun bonus. Équipement d'Abonnement Juillet 2025.",
"headArmoireFlyFishingHatText": "Chapeau de Pêche à la Mouche",
"shieldArmoireFlyFishingRodText": "Canne à Mouche",
"backMystery202507Text": "Planche de Skate Stylée",
"backMystery202507Notes": "Votre fidèle destrier pour affronter le bitume et les rampes. Ne confère aucun bonus. Équipement d'Abonnement Juillet 2025.",
"weaponMystery202508Notes": "Cette lame tournoyante terrifiera tou·te·s monstres et Quotidiennes rouges que vous croiserez sur votre chemin ! Ne confère aucun bonus. Équipement d'Abonnement Août 2025.",
"weaponMystery202508Text": "Lame Lumineuse Pourpe",
"armorArmoireRedWaistcoatText": "Gilet Rouge",
"armorArmoireRedWaistcoatNotes": "Profitez d'un look classe et flamboyant en effectuant vos tâches. Il y a quelque chose de caché dans la poche intérieur, qu'est-ce que ça pourrait être ? Augmente la Constitution et la Force de <%= attrs %> chacune. Armoire Enchantée : Ensemble Gilet Rouge (Objet 2 sur 2)",
"headArmoireRedNewsieHatText": "Casquette Rouge d·u·e la Crieu·r·se Publi·c·que",
"headArmoireRedNewsieHatNotes": "Extra ! Extra ! Toutes les dernières infos : cette casquette est confortable, à la mode, et pratique. Augmente la Perception et l'Intelligence de <%= attrs %> chacune. Armoire Enchantée : Ensemble Gilet Rouge (Objet 1 sur 2)",
"shieldMystery202508Text": "Lame Lumineuse Cyan",
"shieldMystery202508Notes": "Si vous pensiez que c'était classe de faire tournoyer une lame, essayez avec deux ! Ne confère aucun bonus. Équipement d'Abonnement Août 2025."
} }

View File

@@ -55,7 +55,6 @@
"APIToken": "Jeton d'API (ceci est un mot de passe - voir l'avertissement ci-dessus !)", "APIToken": "Jeton d'API (ceci est un mot de passe - voir l'avertissement ci-dessus !)",
"showAPIToken": "Montrer le jeton d'API", "showAPIToken": "Montrer le jeton d'API",
"hideAPIToken": "Cacher le jeton d'API", "hideAPIToken": "Cacher le jeton d'API",
"APITokenWarning": "Si vous avez besoin d'un nouveau jeton d'API (par exemple si vous l'avez partagé par erreur), envoyez un courriel à <%= hrefTechAssistanceEmail %> avec votre ID d'utilisateur et votre jeton d'API actuel. Une fois celui-ci réinitialisé, vous devrez tout ré-autoriser en vous déconnectant du site web et de l'application mobile, et en fournissant le nouveau jeton à tous les outils Habitica que vous utilisez.",
"thirdPartyApps": "Applications tierces", "thirdPartyApps": "Applications tierces",
"resetDo": "Allez-y, réinitialisez mon compte !", "resetDo": "Allez-y, réinitialisez mon compte !",
"resetComplete": "Réinitialisation terminée !", "resetComplete": "Réinitialisation terminée !",
@@ -240,7 +239,7 @@
"addWebhook": "Ajouter un Webhook", "addWebhook": "Ajouter un Webhook",
"changeEmailDisclaimer": "C'est l'adresse courriel que vous utilisez pour vous connecter à Habitica, ainsi que pour recevoir des notifications.", "changeEmailDisclaimer": "C'est l'adresse courriel que vous utilisez pour vous connecter à Habitica, ainsi que pour recevoir des notifications.",
"changeDisplayNameDisclaimer": "C'est le nom qui sera affiché pour votre Avatar dans Habitica.", "changeDisplayNameDisclaimer": "C'est le nom qui sera affiché pour votre Avatar dans Habitica.",
"changePasswordDisclaimer": "Le Mot de passe doit faire 8 caractères ou plus. Nous recommandons d'utiliser un Mot de passe fort que vous n'utilisez nul part ailleurs.", "changePasswordDisclaimer": "Les mots de passe doivent contenir 8 caractères ou plus. Changer votre mot de passe vous déconnectera des autres plateformes et add-ons sur lesquels vous êtes connecté·e.",
"dateFormatDisclaimer": "Ajuster le format de la date sur tout Habitica.", "dateFormatDisclaimer": "Ajuster le format de la date sur tout Habitica.",
"enableAudio": "Activer l'Audio", "enableAudio": "Activer l'Audio",
"playDemoAudio": "Jouer la Démo", "playDemoAudio": "Jouer la Démo",
@@ -254,7 +253,7 @@
"connect": "Se Connecter", "connect": "Se Connecter",
"remove": "Retirer", "remove": "Retirer",
"resetDetail4": "Vous allez perdre tout votre équipement sauf vos Objets Mystères d'Abonné·e et les Objets commémoratifs gratuits. Vous pourrez racheter les objets supprimés, y compris tous les équipements d'édition limitée (vous devez être dans la classe correspondante pour racheter les Équipements spécifique à la classe).", "resetDetail4": "Vous allez perdre tout votre équipement sauf vos Objets Mystères d'Abonné·e et les Objets commémoratifs gratuits. Vous pourrez racheter les objets supprimés, y compris tous les équipements d'édition limitée (vous devez être dans la classe correspondante pour racheter les Équipements spécifique à la classe).",
"APITokenDisclaimer": "<b>Votre Clé API est comme un mot de passe ; ne le partagez pas en public.</b> Il pourra occasionnellement vous être demandé votre ID d'Utilisat·eur·rice, mais ne postez jamais votre Clé API où d'autres personnes pourront le voir, y compris sur Github. <br><br><b>À noter :</b> Si vous avez besoin d'une nouvelle Clé API (si vous l'avez accidentellement partagé, par exemple), écrivez un email à <a href='mailto:admin@habitica.com' target='_blank'>admin@habitica.com</a> avec votre ID d'Utilisat·eur·rice and Clé API acutelle. Une fois réinitialisée, vous devez tout ré-autoriser en vous déconnectant du site web et de l'appli mobile en fournissant la nouvelle Clé aux autres outils Habitica que vous utilisez.", "APITokenDisclaimer": "<b>Votre Clé API est comme un mot de passe ; ne le partagez pas sur des plateformes publiques.</b> Votre ID Utilisat·eur·rice pourrait occasionnellement vous être demandé, mais ne postez jamais votre Clé API où d'autres personnes pourront le voir, y compris sur Github. <br><br><b>Si vous avez besoin d'une nouvelle Clé API </b> (si vous l'avez accidentellement partagé, par exemple), vous pouvez modifier votre mot de passe pour la réinitialiser. Une fois cela fait, vous devrez vous reconnecter sur tous les appareils sur lesquels vous utilisez Habitica et fournir votre nouvelle Clé API aux add-ons que vous utilisez.",
"contentRelease": "Sorties de contenu + Évènements", "contentRelease": "Sorties de contenu + Évènements",
"resetTextSocial": "Si vous êtes absolument sûr·e, tapez <b>\"<%= magicWord %>\"</b> dans le champ de texte ci-dessous.", "resetTextSocial": "Si vous êtes absolument sûr·e, tapez <b>\"<%= magicWord %>\"</b> dans le champ de texte ci-dessous.",
"resetTextLocal": "Si vous êtes absolument sûr·e, tapez votre mot de passe dans le champ de texte ci-dessous.", "resetTextLocal": "Si vous êtes absolument sûr·e, tapez votre mot de passe dans le champ de texte ci-dessous.",

View File

@@ -21,7 +21,7 @@
"subGemName": "Gemmes d'abonnement", "subGemName": "Gemmes d'abonnement",
"maxBuyGems": "Vous avez acheté toutes vos gemmes pour ce mois-ci. De nouvelles sont remises en circulation chaque mois, au cours des trois premiers jours !", "maxBuyGems": "Vous avez acheté toutes vos gemmes pour ce mois-ci. De nouvelles sont remises en circulation chaque mois, au cours des trois premiers jours !",
"timeTravelers": "Voyageurs temporels", "timeTravelers": "Voyageurs temporels",
"timeTravelersPopoverNoSubMobile": "Il semblerait que vous ayez besoin d'un sablier mystique pour ouvrir le portail temporel et invoquer les mystérieux voyageurs temporels.", "timeTravelersPopoverNoSubMobile": "Les Abonné·e·s reçoivent un Sablier Mystique à dépenser dans la Boutique des Voyageu·r·se·s Temporel·le·s tous les mois !",
"timeTravelersPopover": "Votre sablier mystique a ouvert notre portail temporel ! Choisissez ce que vous voudriez que l'on récupère dans le passé ou dans le futur.", "timeTravelersPopover": "Votre sablier mystique a ouvert notre portail temporel ! Choisissez ce que vous voudriez que l'on récupère dans le passé ou dans le futur.",
"mysterySetNotFound": "Ensemble mystère non trouvé, ou ensemble déjà possédé.", "mysterySetNotFound": "Ensemble mystère non trouvé, ou ensemble déjà possédé.",
"mysteryItemIsEmpty": "Les objets mystère sont vides", "mysteryItemIsEmpty": "Les objets mystère sont vides",
@@ -264,5 +264,7 @@
"mysterySet202503": "Ensemble Poids Lourd de Jade", "mysterySet202503": "Ensemble Poids Lourd de Jade",
"mysterySet202504": "Ensemble Yéti Insaisissable", "mysterySet202504": "Ensemble Yéti Insaisissable",
"mysterySet202505": "Ensemble Machaon Majestueux", "mysterySet202505": "Ensemble Machaon Majestueux",
"mysterySet202506": "Ensemble Rayonnement Solaire" "mysterySet202506": "Ensemble Rayonnement Solaire",
"mysterySet202507": "Ensemble Skateu·r·se Stylé·e",
"mysterySet202508": "Ensemble Lame Lumineuse"
} }

Some files were not shown because too many files have changed in this diff Show More