Compare commits

...

56 Commits

Author SHA1 Message Date
Kalista Payne 963b4133ec fix(text): clean up some gear descriptions 2026-02-19 15:48:41 -06:00
Kalista Payne 53999a5b80 fix(content): add seasonal set tokens 2026-02-19 15:46:07 -06:00
Kalista Payne 4510c90e41 feat(content): March-May 2026 2026-02-19 15:46:07 -06:00
Kalista Payne 9f97a09b8c fix(export): remove deprecated routes 2026-02-19 15:45:38 -06:00
Phillip Thelen eccc115b73 Admin Panel fixes (#15613)
* fix profile link to admin panel

* fix profile looking broken when no background is equipped
2026-02-19 11:11:25 -06:00
Tanmay Nalawade 2b26eb2bd1 Habit and Daily task counts not displaying for certain filters (#15595)
* habits-all fixed

* dailies section fixed

* to do counter fixed

* Remove linting changes and apply logic fix with single quotes

* refactor(tasks): simpler badgeCount logic

* fix(lint): remove unused import

---------

Co-authored-by: Kalista Payne <kalista@habitica.com>
2026-02-19 11:09:40 -06:00
Kalista Payne 8e042cabc4 5.44.3 2026-02-10 17:37:48 -06:00
Kalista Payne 8abe167848 chore(subproj): update habitica-images 2026-02-10 17:37:45 -06:00
Kalista Payne 3414f962e2 fix(lint): string style and whitespace 2026-02-10 17:13:02 -06:00
Kalista Payne 1b68e6d4d3 fix(event): show Spring Seasonal Shop on Valentines 2026-02-10 17:07:47 -06:00
Kalista Payne 5dd9711413 Update local dev MongoDB versions (#15334)
* chore(mongodb): update local dev MongoDB versions

* fix(github): update mongodb-github-action

* test(api): attempt to gather more detail on failures

* Revert "test(api): attempt to gather more detail on failures"

This reverts commit 215e768e90.

* WIP mongodb-memory-serve

* fix(mongo): start replica set

* run mongo as gh action service

* remove matrix for mongo

* try npm -> docker instead of services

* try "docker compose"

* disable mongo bootstrap from build

* try gh action again

* try newer action version

* working mongo docker compose 🎉

* fix(lint): leave out unused imports

* update lock

* cleanup previous workflow changes

* remove previous code, dont share mongo data folders on runtype (rs and docker)

* mongo docker for testing; align mongodb directory naming

* remove run-rs, add docker:aio script call, use healthcheck to initiate again

* merge docker-compose.yml, fix client port listening

* fix oudated healthcheck param

* chore(mongodb): update local dev MongoDB versions

* fix(github): update mongodb-github-action

* test(api): attempt to gather more detail on failures

* Revert "test(api): attempt to gather more detail on failures"

This reverts commit 215e768e90.

* WIP mongodb-memory-serve

* run mongo as gh action service

* fix(mongo): start replica set

* remove matrix for mongo

* try npm -> docker instead of services

* try "docker compose"

* disable mongo bootstrap from build

* try gh action again

* try newer action version

* working mongo docker compose 🎉

* fix(lint): leave out unused imports

* update lock

* cleanup previous workflow changes

* remove previous code, dont share mongo data folders on runtype (rs and docker)

* mongo docker for testing; align mongodb directory naming

* remove run-rs, add docker:aio script call, use healthcheck to initiate again

* merge docker-compose.yml, fix client port listening

* fix oudated healthcheck param

* fix(config): remove dup keys

* using npx vite during docker aio run

---------

Co-authored-by: negue <eugen.bolz@gmail.com>
2026-01-30 17:19:35 -06:00
Kalista Payne a542277a41 5.44.2 2026-01-26 11:46:03 -06:00
Phillip Thelen cdf8556fd6 Improve performance in production setup (#15594)
* build cached content files for mobile during gulp build

* load already cached content files during startup

* add option for mongoose to define minPoolSize

* cache client index.html for 10 minutes. Improves initial load times

* add option to auth to use lean version of user doc

* add a way to produce a heapdump from the command line

* fix lint
2026-01-26 11:45:44 -06:00
Kalista Payne 3d93390a7a fix(languages): update template characters 2026-01-23 13:09:14 -06:00
Kalista Payne 59f9cfa0f4 5.44.1 2026-01-23 13:03:30 -06:00
Kalista Payne 80d7804f69 fix(strings): use consistent template characters 2026-01-23 12:53:29 -06:00
Kalista Payne 4e5efe09a3 5.44.0 2026-01-22 11:53:20 -06:00
Fiz d42a597672 Merge pull request #15590 from HabitRPG/kalista/paypal-debug
PayPal debug
2026-01-21 16:11:41 -06:00
Phillip Thelen ea17b2e9c7 Rework how strings are localized (#15589)
* replace lodash template usage with micromustache

* remove function brackets from translations

* add newline

* remove old test

* split core translations from content translations

* fix directory not existing

* fix lint
2026-01-21 14:34:25 -06:00
Phillip Thelen f56708cd88 fix most customizations not being pinnable (#15578)
* fix most customizations not being pinnable

* set correct pinTypes

* fix(pinning): correct purchase types for base hair and mustaches

* automatically unpin purchased customizations

* ability to pin customization items

* Fix pin not showing on buy modal

* Pin on buy modal tweak

---------

Co-authored-by: Kalista Payne <kalista@habitica.com>
Co-authored-by: Hafiz <hafizbhamidi@gmail.com>
2026-01-21 13:57:42 -06:00
Kalista Payne 005d14f6e8 5.43.4 2026-01-20 14:36:36 -06:00
Weblate c05a96ce6c Translated using Weblate (Polish)
Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Portuguese)

Currently translated at 23.7% (60 of 253 strings)

Translated using Weblate (Portuguese)

Currently translated at 23.7% (60 of 253 strings)

Translated using Weblate (Portuguese)

Currently translated at 23.7% (60 of 253 strings)

Translated using Weblate (Portuguese)

Currently translated at 90.5% (183 of 202 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.1% (113 of 114 strings)

Translated using Weblate (Portuguese)

Currently translated at 27.6% (70 of 253 strings)

Translated using Weblate (Portuguese)

Currently translated at 92.9% (106 of 114 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (253 of 253 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 94.8% (3318 of 3497 strings)

Translated using Weblate (Ukrainian)

Currently translated at 98.6% (143 of 145 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 94.8% (3317 of 3497 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 96.8% (245 of 253 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 94.0% (190 of 202 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Portuguese)

Currently translated at 50.1% (1753 of 3497 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3497 of 3497 strings)

Translated using Weblate (Indonesian)

Currently translated at 97.1% (238 of 245 strings)

Translated using Weblate (Ukrainian)

Currently translated at 79.3% (150 of 189 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.5% (928 of 932 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (932 of 932 strings)

Co-authored-by: Ashlynn <ashlynn.samuella@gmail.com>
Co-authored-by: Cezary Polakowski <arutha86@gmail.com>
Co-authored-by: Daniel Costa Carvalho <danielcostacarvalho@gmail.com>
Co-authored-by: Illana Beatriz Rocha de Oliveira <dev.illanabeatriz@gmail.com>
Co-authored-by: Lu dG <ludgs@outlook.fr>
Co-authored-by: Lyam Santos Peres <karinesanper@gmail.com>
Co-authored-by: Maria Morant <luisa.morant@yahoo.com>
Co-authored-by: Nina Łapaj <ninapaj@gmail.com>
Co-authored-by: Ri Vargas <goldenhaitang@gmail.com>
Co-authored-by: Sonia <sophishport@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: いんこ <ayakabooker@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/character/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/character/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/front/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/id/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/uk/
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Character
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Npc
Translation: Habitica/Tasks
2026-01-20 21:33:14 +01:00
Kalista Payne 8fdbfb9dc6 fix(lint): import order 2026-01-16 17:47:05 -06:00
Kalista Payne 057a642baa fix(logger): path 2026-01-16 17:43:54 -06:00
Kalista Payne 6c522157a7 feat(payments): log some anonymous data to chase a bug 2026-01-16 17:38:16 -06:00
Fiz ba9a1ab2a9 Merge pull request #15583 from HabitRPG/kalista/username-sanitize
Sanitize usernames come from social auth
2026-01-15 12:00:56 -06:00
Kalista Payne 4767461c4f 5.43.3 2026-01-14 14:19:54 -06:00
Weblate 847c97dc8f Translated using Weblate (Portuguese (Brazil))
Currently translated at 94.7% (3314 of 3497 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (189 of 189 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (932 of 932 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (932 of 932 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (932 of 932 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (862 of 862 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (288 of 288 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (288 of 288 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (275 of 275 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (276 of 276 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (288 of 288 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 94.7% (3314 of 3497 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (253 of 253 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (862 of 862 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (202 of 202 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (932 of 932 strings)

Translated using Weblate (Indonesian)

Currently translated at 93.0% (228 of 245 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (145 of 145 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (202 of 202 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (932 of 932 strings)

Translated using Weblate (Polish)

Currently translated at 98.9% (272 of 275 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (202 of 202 strings)

Translated using Weblate (Danish)

Currently translated at 88.1% (178 of 202 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (288 of 288 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (202 of 202 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (202 of 202 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (202 of 202 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (145 of 145 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (145 of 145 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (58 of 58 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (932 of 932 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (932 of 932 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (932 of 932 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (932 of 932 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (932 of 932 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (167 of 167 strings)

Translated using Weblate (Danish)

Currently translated at 66.5% (183 of 275 strings)

Translated using Weblate (German)

Currently translated at 98.9% (3461 of 3497 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (253 of 253 strings)

Translated using Weblate (Danish)

Currently translated at 92.5% (175 of 189 strings)

Translated using Weblate (Danish)

Currently translated at 9.0% (23 of 253 strings)

Translated using Weblate (German)

Currently translated at 99.0% (200 of 202 strings)

Translated using Weblate (German)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Danish)

Currently translated at 62.5% (172 of 275 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Japanese)

Currently translated at 96.5% (195 of 202 strings)

Translated using Weblate (Japanese)

Currently translated at 98.4% (249 of 253 strings)

Translated using Weblate (French)

Currently translated at 100.0% (3497 of 3497 strings)

Translated using Weblate (German)

Currently translated at 98.3% (3440 of 3497 strings)

Translated using Weblate (French)

Currently translated at 100.0% (253 of 253 strings)

Translated using Weblate (French)

Currently translated at 100.0% (202 of 202 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 94.7% (3314 of 3497 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (253 of 253 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (202 of 202 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (253 of 253 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (202 of 202 strings)

Co-authored-by: Ashlynn <ashlynn.samuella@gmail.com>
Co-authored-by: Douglas Meneghetti <douglasrizzom@gmail.com>
Co-authored-by: Gabriela <gabisouzars5@gmail.com>
Co-authored-by: Helene Bæck Christensen <helene.baeck@gmail.com>
Co-authored-by: Henrique Ferreira <pedroferreira217.ph@gmail.com>
Co-authored-by: Illana Beatriz Rocha de Oliveira <dev.illanabeatriz@gmail.com>
Co-authored-by: Isabela de Carvalho <isabela.c.escritora@gmail.com>
Co-authored-by: Joanna K <joanna.kociolek0@gmail.com>
Co-authored-by: Kenvinn <kevinsavio514@gmail.com>
Co-authored-by: Lucas Rafaldini <lucas.rafaldini@gmail.com>
Co-authored-by: Luã Fhelyp Guimarães <fhelypg@gmail.com>
Co-authored-by: Lyam Santos Peres <kaka1213spaenrteoss@gmail.com>
Co-authored-by: Lyam Santos Peres <karinesanper@gmail.com>
Co-authored-by: Maycon Douglas Silva Santos <maycondss@live.com>
Co-authored-by: Ri Vargas <goldenhaitang@gmail.com>
Co-authored-by: Rodrigo Gonçalves Braga <rgbraga@gmail.com>
Co-authored-by: Sophie LE MASLE <sophiesuff@gmail.com>
Co-authored-by: Summer_GUI <heyang94@163.com>
Co-authored-by: Thiago Braga <thibraga06@gmail.com>
Co-authored-by: Toro Mor <thomas.bizer@gmx.de>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: purea <g@agaric.eu>
Co-authored-by: いんこ <ayakabooker@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/de/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/id/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/character/da/
Translate-URL: https://translate.habitica.com/projects/habitica/character/de/
Translate-URL: https://translate.habitica.com/projects/habitica/character/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/character/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/character/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/character/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/character/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/id/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/death/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/da/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/id/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/front/da/
Translate-URL: https://translate.habitica.com/projects/habitica/front/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/id/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/da/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/pt_BR/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Character
Translation: Habitica/Communityguidelines
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
2026-01-14 21:18:58 +01:00
Kalista Payne 215b26acac fix(auth): run the actual verify check 2026-01-13 17:01:09 -06:00
Phillip Thelen e223e7821a add config to disable ssl and base_url enforcement (#15587) 2026-01-13 16:44:23 -06:00
Fiz 8134fa7c00 Chat optimization (#15545)
* fix(content): textual tweaks and updates

* fix(link): direct to FAQ instead of wiki

* fix(faq): correct Markdown

* Show orb of rebirth confirmation modal after use (window refresh)

* Set and check rebirth confirmation modal from localstorage

Set and check rebirth confirmation modal from localstorage after window reload

* Don't show orb of rebirth confirmation modal until page reloads

* message effective limit optimization

* Keep max limit for web (400 recent messages)

* Fix amount of messages initially being shown

* PM_PER_PAGE set to 50

* Increases number of messages in inbox test

* Increases number of messages for inbox pagination test

* Set and check rebirth confirmation modal from localstorage

Set and check rebirth confirmation modal from localstorage after window reload

* Don't show orb of rebirth confirmation modal until page reloads

* message effective limit optimization

* Keep max limit for web (400 recent messages)

* Add UUID validation for 'before' query parameter

* add party message stress test tool in admin panel

* lint

* add MAX_PM_COUNT of 400, admin tool for stress testing messages

* comment

* update stress test inbox message tool to use logged in user

* comment

---------

Co-authored-by: Kalista Payne <kalista@habitica.com>
2026-01-13 14:43:09 -06:00
Kalista Payne 84208f612e 5.43.2 2026-01-09 10:43:36 -06:00
Hafiz 57e06334c0 Update allocation option label 2026-01-09 09:05:00 -06:00
Kalista Payne be695d25b3 5.43.1 2026-01-08 16:08:52 -06:00
Kalista Payne 77ee83f467 fix(privacy): ensure opt-in for analytics in modal 2026-01-08 15:40:09 -06:00
Kalista Payne 86556e346b 5.43.0 2026-01-08 14:22:27 -06:00
Weblate 2007a872c6 Merge branch 'origin/develop' into Weblate. 2026-01-08 21:20:01 +01:00
Weblate a8348038de Translated using Weblate (Italian)
Currently translated at 80.8% (2829 of 3497 strings)

Translated using Weblate (Italian)

Currently translated at 80.4% (2813 of 3497 strings)

Translated using Weblate (French)

Currently translated at 100.0% (202 of 202 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (145 of 145 strings)

Translated using Weblate (Italian)

Currently translated at 79.5% (2781 of 3497 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (202 of 202 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Indonesian)

Currently translated at 96.4% (110 of 114 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (145 of 145 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (288 of 288 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 94.6% (3310 of 3497 strings)

Translated using Weblate (German)

Currently translated at 98.3% (3438 of 3497 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (202 of 202 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (French)

Currently translated at 100.0% (202 of 202 strings)

Translated using Weblate (French)

Currently translated at 100.0% (145 of 145 strings)

Translated using Weblate (French)

Currently translated at 100.0% (245 of 245 strings)

Translated using Weblate (French)

Currently translated at 93.0% (188 of 202 strings)

Translated using Weblate (French)

Currently translated at 100.0% (114 of 114 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (Italian)

Currently translated at 100.0% (932 of 932 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 93.4% (3268 of 3497 strings)

Translated using Weblate (Korean)

Currently translated at 99.7% (930 of 932 strings)

Translated using Weblate (Korean)

Currently translated at 72.1% (622 of 862 strings)

Translated using Weblate (Korean)

Currently translated at 75.8% (326 of 430 strings)

Translated using Weblate (Korean)

Currently translated at 97.7% (129 of 132 strings)

Translated using Weblate (Korean)

Currently translated at 37.2% (92 of 247 strings)

Translated using Weblate (Korean)

Currently translated at 93.6% (104 of 111 strings)

Translated using Weblate (Korean)

Currently translated at 97.5% (909 of 932 strings)

Translated using Weblate (Korean)

Currently translated at 91.4% (86 of 94 strings)

Translated using Weblate (Korean)

Currently translated at 60.9% (173 of 284 strings)

Translated using Weblate (Korean)

Currently translated at 72.5% (312 of 430 strings)

Translated using Weblate (Korean)

Currently translated at 49.7% (1739 of 3497 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Korean)

Currently translated at 63.7% (58 of 91 strings)

Translated using Weblate (Turkish)

Currently translated at 55.5% (160 of 288 strings)

Translated using Weblate (Turkish)

Currently translated at 34.8% (86 of 247 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 76.1% (188 of 247 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 92.7% (3243 of 3497 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 92.6% (3240 of 3497 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 92.4% (3233 of 3497 strings)

Translated using Weblate (Turkish)

Currently translated at 72.7% (627 of 862 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (276 of 276 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (862 of 862 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (288 of 288 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 92.3% (3229 of 3497 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 92.3% (3229 of 3497 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (932 of 932 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 90.5% (3165 of 3497 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 90.5% (3165 of 3497 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.6% (284 of 288 strings)

Translated using Weblate (Korean)

Currently translated at 70.7% (610 of 862 strings)

Translated using Weblate (Korean)

Currently translated at 71.3% (307 of 430 strings)

Translated using Weblate (Korean)

Currently translated at 49.6% (1737 of 3497 strings)

Translated using Weblate (Turkish)

Currently translated at 71.6% (618 of 862 strings)

Translated using Weblate (Romanian)

Currently translated at 56.5% (156 of 276 strings)

Translated using Weblate (Romanian)

Currently translated at 91.2% (104 of 114 strings)

Translated using Weblate (Romanian)

Currently translated at 98.2% (57 of 58 strings)

Translated using Weblate (Japanese)

Currently translated at 98.1% (3432 of 3497 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (114 of 114 strings)

Translated using Weblate (Turkish)

Currently translated at 63.9% (551 of 862 strings)

Translated using Weblate (Japanese)

Currently translated at 97.9% (3427 of 3497 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (430 of 430 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 100.0% (275 of 275 strings)

Translated using Weblate (Japanese)

Currently translated at 97.8% (3423 of 3497 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 98.2% (57 of 58 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 89.3% (218 of 244 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 77.2% (146 of 189 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 82.1% (708 of 862 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 96.7% (88 of 91 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 95.4% (106 of 111 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 71.6% (197 of 275 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (3497 of 3497 strings)

Translated using Weblate (Spanish)

Currently translated at 99.9% (3495 of 3497 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (132 of 132 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Turkish)

Currently translated at 87.3% (241 of 276 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (91 of 91 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (111 of 111 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (276 of 276 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 69.9% (2446 of 3497 strings)

Translated using Weblate (German)

Currently translated at 98.2% (3436 of 3497 strings)

Translated using Weblate (Turkish)

Currently translated at 74.6% (206 of 276 strings)

Translated using Weblate (Turkish)

Currently translated at 74.6% (206 of 276 strings)

Translated using Weblate (Turkish)

Currently translated at 69.5% (192 of 276 strings)

Translated using Weblate (Turkish)

Currently translated at 65.9% (182 of 276 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (193 of 193 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (932 of 932 strings)

Translated using Weblate (Turkish)

Currently translated at 85.5% (797 of 932 strings)

Co-authored-by: Ashlynn <ashlynn.samuella@gmail.com>
Co-authored-by: Begümay Çınar <begumay@proton.me>
Co-authored-by: Céu <marcel.ufscar@gmail.com>
Co-authored-by: Douglas Meneghetti <douglasrizzom@gmail.com>
Co-authored-by: Frodo <Zatriel@outlook.com.br>
Co-authored-by: Israel Silva Araújo <yisrael.araujo@gmail.com>
Co-authored-by: Jaime Martí <jaumemarti77@icloud.com>
Co-authored-by: John B <habitica.quarters574@passmail.net>
Co-authored-by: Lucas Lo <lucas1020.rabbit@gmail.com>
Co-authored-by: Luizo <t.czj2019@gmail.com>
Co-authored-by: Lyam Santos Peres <kaka1213spaenrteoss@gmail.com>
Co-authored-by: Márcio Ramos Corrêa <marcio.ramos.correa@gmail.com>
Co-authored-by: Omar Bertolla <scaram@icloud.com>
Co-authored-by: Sefa Uğurlu <ugurlusefa2@gmail.com>
Co-authored-by: Seohee <ka011215@naver.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: Trần Thu Trúc <ngw.nah@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: ‌ <e0gla0f@gmail.com>
Co-authored-by: いんこ <ayakabooker@gmail.com>
Co-authored-by: 張鈞崴 <qoo94230@gmail.com>
Co-authored-by: 강아름 <sick_kar@naver.com>
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/it/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/id/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/character/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/character/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/character/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/character/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/character/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/id/
Translate-URL: https://translate.habitica.com/projects/habitica/death/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/de/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/it/
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/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/id/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/id/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ko/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/en_GB/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ro/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/zh_Hans/
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Character
Translation: Habitica/Communityguidelines
Translation: Habitica/Contrib
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
2026-01-08 20:28:08 +01:00
Hafiz 87bcd69979 stat allocation wording 2026-01-08 12:35:31 -06:00
Hafiz 8f8e84d0c7 Stat allocation selection lowercase/capitalization 2026-01-08 11:51:21 -06:00
Hafiz 2c18cb00cc capitalization 2026-01-08 11:27:35 -06:00
Hafiz daa0fd18c0 Remove "points" from task allocation dropdown item title 2026-01-07 19:30:20 -06:00
Kalista Payne 5c555cbf88 fix(auth): revert attempted perf tweak 2026-01-07 17:52:33 -06:00
Kalista Payne 7379c7b230 fix(auth): handle potential collisions on username 2026-01-07 17:26:42 -06:00
Kalista Payne c055537c38 fix(lint): whitespace 2026-01-07 16:49:22 -06:00
Kalista Payne 7559feec8e Revert "fix(lint): whitespace; also revert username fix for further QA"
This reverts commit dcd15a58ebf41d9e44c89c675419ecb7d36b946d.
2026-01-07 16:49:22 -06:00
Kalista Payne 43808696a8 fix(lint): whitespace; also revert username fix for further QA 2026-01-07 16:49:22 -06:00
Kalista Payne 72fb41c7e0 fix(lint): missing variable declaration 2026-01-07 16:45:19 -06:00
Kalista Payne 3bf18e09ed fix(auth): strip invalid characters during social reg 2026-01-07 16:45:19 -06:00
Kalista Payne 407a901883 feat(faq): add detail on stats and allocation 2026-01-07 15:30:37 -06:00
Kalista Payne 81a008906b fix(lint): max-len 2026-01-07 14:36:51 -06:00
Kalista Payne 992a978923 Squashed commit of the following:
commit dcd15a58ebf41d9e44c89c675419ecb7d36b946d
Author: Kalista Payne <kalista@habitica.com>
Date:   Wed Jan 7 14:11:29 2026 -0600

    fix(lint): whitespace; also revert username fix for further QA

commit 8af109f3c358418a778d4dc8387524840343f046
Author: Kalista Payne <kalista@habitica.com>
Date:   Mon Jan 5 15:04:30 2026 -0600

    fix(footer): better hover looks

commit 87518d335967a90d3049cfde9160b341cbf55417
Author: Kalista Payne <kalista@habitica.com>
Date:   Tue Dec 30 15:10:14 2025 -0600

    fix(footer): change to flex layout for better alignment

commit 45ab33c1afaa5b34c56fb09cdcd0f4c12fee0893
Author: Kalista Payne <kalista@habitica.com>
Date:   Tue Dec 30 13:03:56 2025 -0600

    fix(lint): whitespace

commit c9d20e86d29d89b881813a2579ec1ba61acc82cc
Author: Kalista Payne <kalista@habitica.com>
Date:   Tue Dec 30 12:54:04 2025 -0600

    fix(lint): unused var and whitespace

commit 05dc11fd11a530b0b460a951a8be32fe6f4e190e
Author: Kalista Payne <kalista@habitica.com>
Date:   Tue Dec 30 12:48:26 2025 -0600

    fix(footer): rearrange social block, clean up unused donate css

commit 131cda6bb99b4eb53fdadf551bfddc3f712e81e8
Author: Kalista Payne <kalista@habitica.com>
Date:   Mon Dec 29 16:19:18 2025 -0600

    fix(login): move password flows into static wrapper

commit 563ed9fa02a34bbad53421795415e43c955813c1
Author: Kalista Payne <kalista@habitica.com>
Date:   Mon Dec 29 16:02:41 2025 -0600

    fix(login): resetPassword not reset-password route name

commit d23b64a03ec48b5634f05b4500153ed54a0ba6e7
Author: Kalista Payne <kalista@habitica.com>
Date:   Mon Dec 29 15:48:51 2025 -0600

    fix(login): add missing footer, remove dono button

commit 55e841252fbaafdccf75828b7ce87946c185792a
Author: Kalista Payne <kalista@habitica.com>
Date:   Fri Dec 19 15:58:52 2025 -0600

    fix(passwords): standardize styling for reset pass form

commit 7e84a54f104191d6e6d2e86f25070847e1c1c674
Author: Kalista Payne <kalista@habitica.com>
Date:   Tue Dec 16 16:21:50 2025 -0600

    fix(lint): missing variable declaration

commit 777f8f13d7b3b889b5637102b8258b20020c6c6d
Author: Kalista Payne <kalista@habitica.com>
Date:   Tue Dec 16 16:17:19 2025 -0600

    fix(auth): strip invalid characters during social reg
2026-01-07 14:13:47 -06:00
Fiz a8062ad615 Implement stat allocation & auto stat allocation (#15551)
* fix(responsive): adjustments for small screens by @Hafizzle

* Fixed cut off modal issues

- Fixed limited time banner being cut off on smaller devices
- Fixed Pin on purchase quest modal being cut off

* Update order of views on Stats tab

* Update styling of stat boxes & add stat allocation info

* Live stat allocation & auto allocation implemented

- Update stat allocation UI/UX cont.
- Update stats live (versus pressing save)
- Dynamically show the distribute dropdown based on if auto allocate switch is enabled

* Remove duplicate string value, convert line endings

* Fix auto allocation toggle alignment

* lint fixes

* Auto allocate UI fixes

- remove info icon in each dropdown option, and replace info below each option
- disable auto allocate dropdown instead of hiding it
- removed radio buttons drop auto allocate options dropdown
- Points available & auto allocate text share same baseline now

* Stat allocation updates

* Assigned Stat

* lint fixes

* stat feedback UI updates

* tweak

* stat allocation UI tweaks

refactor allocation mode selection with selectList component & other UI tweaks

* Fix hover dropdown padding & stat allocation chip always showing

* Update stat allocation dropdown styling

* Remove bold text on hover

* Stat allocation by task dropdown

* remove empty lines

* show selected attribute on component itself

* task stat selection UI updates

* fix(cr): remove weird line endings

---------

Co-authored-by: Kalista Payne <kalista@habitica.com>
2026-01-06 16:08:34 -06:00
Fiz 781a904583 Replace browser confirmations with confirmation modals (#15544)
* Set and check rebirth confirmation modal from localstorage

Set and check rebirth confirmation modal from localstorage after window reload

* Don't show orb of rebirth confirmation modal until page reloads

* Confirmation prompts (replace browser confirmation prompts)

* lint

* lint

* Fix confirmation modal not showing for items w/gems

* Updated delete and confirmation modals

* lint

* Fix top bar on color on confirmation prompt clipping

* Show currency amount on purchase confirmation modals

* confirmation modals using existing components

* lint

* Broken task confirmation modal

* Replace class change confirmation with modal

* fix(style): correct indentation

* Replace warning svg w/ existing alert svg, and reset language strings

* fix(json): json doesn't like comma dangles

---------

Co-authored-by: Kalista Payne <kalista@habitica.com>
Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2026-01-06 15:19:30 -06:00
Kalista Payne d87946d912 5.42.4 2025-12-19 14:11:46 -06:00
Fiz 7456ff2def Fix g1g1 notification not staying dismissed (#15580)
- use localStorage instead of sessionStorage so dismissal persists across browser sessions
- Add .stop modifier to close button click
2025-12-19 13:57:35 -06:00
367 changed files with 11068 additions and 6253 deletions
+18 -8
View File
@@ -82,7 +82,7 @@ jobs:
CI: true
NODE_ENV: test
- run: npm run test:sanity
common:
runs-on: ubuntu-latest
strategy:
@@ -129,13 +129,13 @@ jobs:
CI: true
NODE_ENV: test
- run: npm run test:content
api-unit:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [21.x]
mongodb-version: [4.2]
mongodb-version: [7.0]
steps:
- uses: actions/checkout@v4
with:
@@ -144,11 +144,13 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Start MongoDB ${{ matrix.mongodb-version }} Replica Set
uses: supercharge/mongodb-github-action@1.3.0
uses: supercharge/mongodb-github-action@1.11.0
with:
mongodb-version: ${{ matrix.mongodb-version }}
mongodb-replica-set: rs
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
@@ -158,15 +160,17 @@ jobs:
env:
CI: true
NODE_ENV: test
- run: npm run test:api:unit
env:
REQUIRES_SERVER=true: true
api-v3-integration:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [21.x]
mongodb-version: [4.2]
mongodb-version: [7.0]
steps:
- uses: actions/checkout@v4
with:
@@ -176,10 +180,11 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- name: Start MongoDB ${{ matrix.mongodb-version }} Replica Set
uses: supercharge/mongodb-github-action@1.3.0
uses: supercharge/mongodb-github-action@1.11.0
with:
mongodb-version: ${{ matrix.mongodb-version }}
mongodb-replica-set: rs
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
@@ -189,15 +194,18 @@ jobs:
env:
CI: true
NODE_ENV: test
- run: npm run test:api-v3:integration
env:
REQUIRES_SERVER=true: true
api-v4-integration:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [21.x]
mongodb-version: [4.2]
mongodb-version: [7.0]
steps:
- uses: actions/checkout@v4
with:
@@ -207,10 +215,11 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- name: Start MongoDB ${{ matrix.mongodb-version }} Replica Set
uses: supercharge/mongodb-github-action@1.3.0
uses: supercharge/mongodb-github-action@1.11.0
with:
mongodb-version: ${{ matrix.mongodb-version }}
mongodb-replica-set: rs
- run: sudo apt update
- run: sudo apt -y install libkrb5-dev
- run: cp config.json.example config.json
@@ -220,6 +229,7 @@ jobs:
env:
CI: true
NODE_ENV: test
- run: npm run test:api-v4:integration
env:
REQUIRES_SERVER=true: true
+1 -1
View File
@@ -47,5 +47,5 @@ webpack.webstorm.config
# mongodb replica set for local dev
mongodb-*.tgz
/mongodb-data*
/mongodb-*
/.nyc_output
+2 -2
View File
@@ -46,7 +46,7 @@
"MAINTENANCE_MODE": "false",
"MONGODB_POOL_SIZE": "10",
"MONGODB_SOCKET_TIMEOUT": "20000",
"NODE_DB_URI": "mongodb://localhost:27017/habitica-dev?replicaSet=rs",
"NODE_DB_URI": "mongodb://localhost:27017/habitica-dev?replicaSet=rs&directConnection=true&readPreference=secondary",
"NODE_ENV": "development",
"PATH": "bin:node_modules/.bin:/usr/local/bin:/usr/bin:/bin",
"PAYPAL_BILLING_PLANS_basic_12mo": "basic_12mo",
@@ -90,7 +90,7 @@
"STRIPE_API_KEY": "aaaabbbbccccddddeeeeffff00001111",
"STRIPE_PUB_KEY": "22223333444455556666777788889999",
"STRIPE_WEBHOOKS_ENDPOINT_SECRET": "111111",
"TEST_DB_URI": "mongodb://localhost:27017/habitica-test?replicaSet=rs",
"TEST_DB_URI": "mongodb://localhost:27017/habitica-test?replicaSet=rs&directConnection=true&readPreference=secondary",
"TIME_TRAVEL_ENABLED": "false",
"TRUSTED_DOMAINS": "localhost,https://habitica.com",
"WEB_CONCURRENCY": 1
-53
View File
@@ -1,53 +0,0 @@
services:
client:
build:
context: .
dockerfile: ./Dockerfile-Dev
command: ["npm", "run", "client:dev"]
depends_on:
- server
environment:
- BASE_URL=http://server:3000
networks:
- habitica
ports:
- "8080:8080"
volumes:
- .:/usr/src/habitica
- /usr/src/habitica/node_modules
- /usr/src/habitica/website/client/node_modules
server:
build:
context: .
dockerfile: ./Dockerfile-Dev
command: ["npm", "start"]
depends_on:
mongo:
condition: service_healthy
environment:
- NODE_DB_URI=mongodb://mongo/habitrpg
networks:
- habitica
ports:
- "3000:3000"
volumes:
- .:/usr/src/habitica
- /usr/src/habitica/node_modules
mongo:
image: mongo:5.0.23
restart: unless-stopped
command: ["--replSet", "rs", "--bind_ip_all", "--port", "27017"]
healthcheck:
test: echo "try { rs.status() } catch (err) { rs.initiate() }" | mongosh --port 27017 --quiet
interval: 10s
timeout: 30s
start_period: 0s
start_interval: 1s
retries: 30
networks:
- habitica
ports:
- "27017:27017"
networks:
habitica:
driver: bridge
+23
View File
@@ -0,0 +1,23 @@
networks:
mongodb-network:
name: "mongodb-network"
driver: bridge
services:
mongodb:
image: "mongo:7.0"
container_name: "habitica-mongodb-only"
networks:
- mongodb-network
hostname: "mongodb"
ports:
- "27017:27017"
restart: "unless-stopped"
volumes:
- "./mongodb-data-docker:/data/db"
entrypoint: [ "/usr/bin/mongod", "--bind_ip_all", "--replSet", "rs" ]
healthcheck:
test: echo "try { rs.status() } catch (err) { rs.initiate() }" | mongosh --port 27017 --quiet
interval: 10s
timeout: 30s
start_period: 0s
retries: 30
+23
View File
@@ -0,0 +1,23 @@
networks:
mongodb-network:
name: "mongodb-network"
driver: bridge
services:
mongodb:
image: "mongo:7.0"
container_name: "habitica-mongodb-test"
networks:
- mongodb-network
hostname: "mongodb"
ports:
- "27017:27017"
restart: "unless-stopped"
volumes:
- "./mongodb-data-docker-testing:/data/db"
entrypoint: [ "/usr/bin/mongod", "--bind_ip_all", "--replSet", "rs" ]
healthcheck:
test: echo "try { rs.status() } catch (err) { rs.initiate() }" | mongosh --port 27017 --quiet
interval: 10s
timeout: 30s
start_period: 0s
retries: 30
+43 -22
View File
@@ -1,35 +1,56 @@
version: "3"
services:
client:
build: .
networks:
- habitica
environment:
- BASE_URL=http://server:3000
ports:
- "8080:8080"
command: ["npm", "run", "client:dev"]
build:
context: .
dockerfile: ./Dockerfile-Dev
command: ["npm", "run", "client:dev:docker"]
depends_on:
- server
server:
build: .
ports:
- "3000:3000"
environment:
- BASE_URL=http://server:3000
networks:
- habitica
ports:
- "5173:5173"
volumes:
- .:/usr/src/habitica
- /usr/src/habitica/node_modules
- /usr/src/habitica/website/client/node_modules
server:
build:
context: .
dockerfile: ./Dockerfile-Dev
command: ["npm", "start"]
depends_on:
mongo:
condition: service_healthy
environment:
- NODE_DB_URI=mongodb://mongo/habitrpg
depends_on:
- mongo
mongo:
image: mongo:3.6
ports:
- "27017:27017"
networks:
- habitica
ports:
- "3000:3000"
volumes:
- .:/usr/src/habitica
- /usr/src/habitica/node_modules
mongo:
image: "mongo:7.0"
container_name: "habitica-mongodb"
networks:
- habitica
hostname: "mongodb"
ports:
- "27017:27017"
restart: "unless-stopped"
volumes:
- "./mongodb-data-docker:/data/db"
entrypoint: [ "/usr/bin/mongod", "--bind_ip_all", "--replSet", "rs" ]
healthcheck:
test: echo "try { rs.status() } catch (err) { rs.initiate() }" | mongosh --port 27017 --quiet
interval: 10s
timeout: 30s
start_period: 0s
retries: 30
networks:
habitica:
+12 -9
View File
@@ -5,7 +5,7 @@ import path from 'path';
import babel from 'gulp-babel';
import os from 'os';
import fs from 'fs';
import spawn from 'cross-spawn'; // eslint-disable-line import/no-extraneous-dependencies
import spawn from 'cross-spawn';
import clean from 'rimraf';
gulp.task('build:babel:server', () => gulp.src('website/server/**/*.js')
@@ -35,7 +35,7 @@ gulp.task('build:prod', gulp.series(
// When used on windows `run-rs` must first be run without the `--keep` option
// in order to be setup correctly, afterwards it can be used.
const MONGO_PATH = path.join(__dirname, '/../mongodb-data/');
const MONGO_PATH = path.join(__dirname, '/../mongodb-data-docker/');
gulp.task('build:prepare-mongo', async () => {
if (fs.existsSync(MONGO_PATH)) {
@@ -51,29 +51,32 @@ gulp.task('build:prepare-mongo', async () => {
console.log('MongoDB data folder is missing, setting up.'); // eslint-disable-line no-console
// use run-rs without --keep, kill it as soon as the replica set starts
const runRsProcess = spawn('run-rs', ['-v', '4.1.1', '-l', 'ubuntu1804', '--dbpath', 'mongodb-data', '--number', '1', '--quiet']);
const dockerMongoProcess = spawn('npm', ['run', 'docker:mongo:dev']);
for await (const chunk of runRsProcess.stdout) {
let manuallyStopped = false;
for await (const chunk of dockerMongoProcess.stdout) {
const stringChunk = chunk.toString();
console.log(stringChunk); // eslint-disable-line no-console
// kills the process after the replica set is setup
if (stringChunk.includes('Started replica set')) {
if (stringChunk.includes('mongod startup complete')) {
console.log('MongoDB setup correctly.'); // eslint-disable-line no-console
runRsProcess.kill();
dockerMongoProcess.kill();
manuallyStopped = true;
}
}
let error = '';
for await (const chunk of runRsProcess.stderr) {
for await (const chunk of dockerMongoProcess.stderr) {
const stringChunk = chunk.toString();
error += stringChunk;
}
const exitCode = await new Promise(resolve => {
runRsProcess.on('close', resolve);
dockerMongoProcess.on('close', resolve);
});
if (exitCode || error.length > 0) {
if (!manuallyStopped && (exitCode || error.length > 0)) {
// remove any leftover files
clean.sync(MONGO_PATH);
+61 -26
View File
@@ -6,9 +6,21 @@ gulp.task('cache:content', done => {
// Requiring at runtime because these files access `common`
// code which in production works only if transpiled so after
// gulp build:babel:common has run
const { CONTENT_CACHE_PATH, getLocalizedContentResponse } = require('../website/server/libs/content'); // eslint-disable-line global-require
const {
CONTENT_CACHE_PATH,
getLocalizedContentResponse,
IOS_FILTER,
ANDROID_FILTER,
buildFilterObject,
hashForFilter,
} = require('../website/server/libs/content'); // eslint-disable-line global-require
const { langCodes } = require('../website/server/libs/i18n'); // eslint-disable-line global-require
const iosHash = hashForFilter(IOS_FILTER);
const iosFilterObj = buildFilterObject(IOS_FILTER);
const androidHash = hashForFilter(ANDROID_FILTER);
const androidFilterObj = buildFilterObject(ANDROID_FILTER);
try {
// create the cache folder (if it doesn't exist)
try {
@@ -26,33 +38,56 @@ gulp.task('cache:content', done => {
getLocalizedContentResponse(langCode),
'utf8',
);
});
done();
} catch (err) {
done(err);
}
});
gulp.task('cache:i18n', done => {
// Requiring at runtime because these files access `common`
// code which in production works only if transpiled so after
// gulp build:babel:common has run
const { BROWSER_SCRIPT_CACHE_PATH, geti18nBrowserScript } = require('../website/server/libs/i18n'); // eslint-disable-line global-require
const { langCodes } = require('../website/server/libs/i18n'); // eslint-disable-line global-require
try {
// create the cache folder (if it doesn't exist)
try {
fs.mkdirSync(BROWSER_SCRIPT_CACHE_PATH);
} catch (err) {
if (err.code !== 'EEXIST') throw err;
}
// create and save the i18n browser script for each language
langCodes.forEach(languageCode => {
fs.writeFileSync(
`${BROWSER_SCRIPT_CACHE_PATH}${languageCode}.js`,
geti18nBrowserScript(languageCode),
`${CONTENT_CACHE_PATH}${langCode}${iosHash}.json`,
getLocalizedContentResponse(langCode, iosFilterObj),
'utf8',
);
fs.writeFileSync(
`${CONTENT_CACHE_PATH}${langCode}${androidHash}.json`,
getLocalizedContentResponse(langCode, androidFilterObj),
'utf8',
);
});
done();
} catch (err) {
done(err);
}
});
function safeMkdir (path) {
try {
fs.mkdirSync(path);
} catch (err) {
if (err.code !== 'EEXIST') throw err;
}
}
gulp.task('cache:i18n', done => {
// Requiring at runtime because these files access `common`
// code which in production works only if transpiled so after
// gulp build:babel:common has run
const { BROWSER_SCRIPT_CACHE_PATH, geti18nCoreBrowserScript, geti18nContentBrowserScript } = require('../website/server/libs/i18n'); // eslint-disable-line global-require
const { langCodes } = require('../website/server/libs/i18n'); // eslint-disable-line global-require
try {
// create the cache folders (if they doesn't exist)
safeMkdir(BROWSER_SCRIPT_CACHE_PATH);
safeMkdir(`${BROWSER_SCRIPT_CACHE_PATH}core/`);
safeMkdir(`${BROWSER_SCRIPT_CACHE_PATH}content/`);
// create and save the i18n browser script for each language
langCodes.forEach(languageCode => {
fs.writeFileSync(
`${BROWSER_SCRIPT_CACHE_PATH}core/${languageCode}.js`,
geti18nCoreBrowserScript(languageCode),
'utf8',
);
fs.writeFileSync(
`${BROWSER_SCRIPT_CACHE_PATH}content/${languageCode}.js`,
geti18nContentBrowserScript(languageCode),
'utf8',
);
});
+5
View File
@@ -53,6 +53,11 @@ gulp.task('test:prepare:mongo', cb => {
const mongooseOptions = getDefaultConnectionOptions();
const connectionUrl = getDevelopmentConnectionUrl(TEST_DB_URI);
console.info({
mongooseOptions,
connectionUrl,
});
mongoose.connect(connectionUrl, mongooseOptions)
.then(() => mongoose.connection.dropDatabase())
.then(() => mongoose.connection.close()).then(() => {
+426 -605
View File
File diff suppressed because it is too large Load Diff
+10 -5
View File
@@ -1,7 +1,7 @@
{
"name": "habitica",
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
"version": "5.42.3",
"version": "5.44.3",
"main": "./website/server/index.js",
"dependencies": {
"@babel/core": "^7.22.10",
@@ -40,6 +40,7 @@
"gulp-imagemin": "^7.1.0",
"gulp.spritesmith": "^6.13.0",
"habitica-markdown": "^3.0.0",
"heapdump": "^0.3.15",
"helmet": "^4.6.0",
"in-app-purchase": "^1.11.3",
"js2xmlparser": "^5.0.0",
@@ -48,10 +49,12 @@
"lodash": "^4.17.21",
"merge-stream": "^2.0.0",
"method-override": "^3.0.0",
"micromustache": "^8.0.3",
"moment": "^2.29.4",
"moment-recur": "git://github.com/HabitRPG/moment-recur.git#d3e8e6da0806f13b74dd2e4d7d9053e6a63db119",
"mongoose": "^8.9.5",
"morgan": "^1.10.1",
"nan": "^2.25.0",
"nconf": "^0.12.1",
"node-gcm": "^1.0.5",
"on-headers": "^1.1.0",
@@ -100,13 +103,16 @@
"coverage": "nyc report --reporter=html --report-dir coverage/results; open coverage/results/index.html",
"sprites": "gulp sprites:compile",
"client:dev": "cd website/client && npm run serve",
"client:dev:docker": "cd website/client && npm run serve:docker",
"client:build": "cd website/client && npm run build",
"client:unit": "cd website/client && npm run test:unit",
"start": "node --watch ./website/server/index.js",
"start:simple": "node ./website/server/index.js",
"debug": "node --watch --inspect ./website/server/index.js",
"mongo:dev": "run-rs -v 7.0.23 -l ubuntu2214 --keep --dbpath mongodb-data --number 1 --quiet",
"mongo:test": "run-rs -v 7.0.23 -l ubuntu2214 --keep --dbpath mongodb-data-testing --number 1 --quiet",
"docker:aio": "docker compose -f docker-compose.yml up",
"docker:mongo:dev": "docker compose -f docker-compose.mongo-only.yml up",
"docker:mongo:dev:down": "docker compose -f docker-compose.mongo-only.yml down",
"docker:mongo:test": "docker compose -f docker-compose.mongo-test-local.yml up",
"mongo:test": "node scripts/start-local-mongo.mjs --test-db",
"postinstall": "git config --global url.\"https://\".insteadOf git:// && gulp build && cd website/client && npm install",
"apidoc": "gulp apidoc",
"heroku-postbuild": ".heroku/report_deploy.sh"
@@ -122,7 +128,6 @@
"monk": "^7.3.4",
"nyc": "^15.1.0",
"require-again": "^2.0.0",
"run-rs": "^0.7.7",
"sinon-chai": "^3.7.0",
"sinon-stub-promise": "^4.0.0"
}
@@ -1,35 +0,0 @@
import { v4 as generateUUID } from 'uuid';
import {
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v3';
xdescribe('GET /export/avatar-:memberId.html', () => {
let user;
before(async () => {
user = await generateUser();
});
it('validates req.params.memberId', async () => {
await expect(user.get('/export/avatar-:memberId.html')).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('invalidReqParams'),
});
});
it('handles non-existing members', async () => {
const dummyId = generateUUID();
await expect(user.get(`/export/avatar-${dummyId}.html`)).to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('userWithIDNotFound', { userId: dummyId }),
});
});
it('returns an html page', async () => {
const res = await user.get(`/export/avatar-${user._id}.html`);
expect(res.substring(0, 100).indexOf('<!DOCTYPE html>')).to.equal(0);
});
});
@@ -1,3 +0,0 @@
// TODO how to test this route since it points to a file on AWS s3?
describe('GET /export/avatar-:memberId.png', () => {});
@@ -47,7 +47,7 @@ describe('GET /inbox/messages', () => {
it('returns four messages when using page-query ', async () => {
const promises = [];
for (let i = 0; i < 10; i += 1) {
for (let i = 0; i < 50; i += 1) {
promises.push(user.post('/members/send-private-message', {
toUserId: user.id,
message: 'fourth',
@@ -65,6 +65,52 @@ describe('POST /user/auth/social', () => {
await expect(getProperty('users', response.id, 'profile.name')).to.eventually.equal('a google user');
});
it('includes sanitized version of provided username', async () => {
const response = await api.post(endpoint, {
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
network,
username: 'Google User Name',
});
await expect(getProperty('users', response.id, 'auth.local.username')).to.eventually.equal('GoogleUserName');
await expect(getProperty('users', response.id, 'auth.local.lowerCaseUsername')).to.eventually.equal('googleusername');
});
it('generates a random username if provided username contains only disallowed characters', async () => {
const response = await api.post(endpoint, {
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
network,
username: 'Áîüè',
});
await expect(getProperty('users', response.id, 'auth.local.username')).to.eventually.contain('hb-');
await expect(getProperty('users', response.id, 'auth.local.lowerCaseUsername')).to.eventually.contain('hb-');
});
it('generates a random username if provided username contains a disallowed word', async () => {
const response = await api.post(endpoint, {
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
network,
username: 'i am a TESTPLACEHOLDERSLURWORDHERE',
});
await expect(getProperty('users', response.id, 'auth.local.username')).to.eventually.contain('hb-');
await expect(getProperty('users', response.id, 'auth.local.lowerCaseUsername')).to.eventually.contain('hb-');
});
it('generates a random username if sanitized username conflicts with an extant user', async () => {
user = await generateUser({ 'auth.local.username': 'GoogleUserName' });
const response = await api.post(endpoint, {
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
network,
username: 'Google User Name',
});
await expect(getProperty('users', response.id, 'auth.local.username')).to.eventually.contain('hb-');
await expect(getProperty('users', response.id, 'auth.local.lowerCaseUsername')).to.eventually.contain('hb-');
});
it('fails if allowRegister is false and user does not exist', async () => {
await expect(api.post(endpoint, {
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
@@ -66,7 +66,7 @@ describe('GET /inbox/conversations', () => {
it('returns five messages when using page-query ', async () => {
const promises = [];
for (let i = 0; i < 10; i += 1) {
for (let i = 0; i < 50; i += 1) {
promises.push(user.post('/members/send-private-message', {
toUserId: user.id,
message: 'fourth',
+9 -1
View File
@@ -20,6 +20,9 @@ describe('shared.ops.unlock', () => {
beforeEach(() => {
user = generateUser();
user.balance = usersStartingGems;
user.pinnedItems.push({ type: 'background', path: 'backgrounds.backgrounds042016.giant_florals' });
user.pinnedItems.push({ type: 'haircolor', path: 'hair.color.rainbow' });
user.pinnedItems.push({ type: 'shirt', path: 'shirt.convict' });
clock = sandbox.useFakeTimers(new Date('2024-04-10'));
});
@@ -272,6 +275,7 @@ describe('shared.ops.unlock', () => {
});
it('unlocks an item (appearance)', async () => {
expect(user.pinnedItems.findIndex(item => item.type === 'shirt')).to.not.equal(-1);
const path = unlockPath.split(',')[0];
const initialShirts = Object.keys(user.purchased.shirt).length;
const [, message] = await unlock(user, { query: { path } });
@@ -282,11 +286,12 @@ describe('shared.ops.unlock', () => {
);
expect(get(user.purchased, path)).to.be.true;
expect(user.balance).to.equal(usersStartingGems - 0.5);
expect(user.pinnedItems.findIndex(item => item.type === 'shirt')).to.equal(-1);
});
it('unlocks an item (hair color)', async () => {
user.purchased.hair.color = {};
expect(user.pinnedItems.findIndex(item => item.type === 'haircolor')).to.not.equal(-1);
const path = hairUnlockPath.split(',')[0];
const initialColorHair = Object.keys(user.purchased.hair.color).length;
const [, message] = await unlock(user, { query: { path } });
@@ -297,6 +302,7 @@ describe('shared.ops.unlock', () => {
);
expect(get(user.purchased, path)).to.be.true;
expect(user.balance).to.equal(usersStartingGems - 0.5);
expect(user.pinnedItems.findIndex(item => item.type === 'haircolor')).to.equal(-1);
});
it('unlocks an item (facial hair)', async () => {
@@ -334,6 +340,7 @@ describe('shared.ops.unlock', () => {
it('unlocks an item (background)', async () => {
const initialBackgrounds = Object.keys(user.purchased.background).length;
expect(user.pinnedItems.findIndex(item => item.type === 'background')).to.not.equal(-1);
const [, message] = await unlock(user, {
query: { path: backgroundUnlockPath },
});
@@ -344,6 +351,7 @@ describe('shared.ops.unlock', () => {
);
expect(get(user.purchased, backgroundUnlockPath)).to.be.true;
expect(user.balance).to.equal(usersStartingGems - 1.75);
expect(user.pinnedItems.findIndex(item => item.type === 'background')).to.equal(-1);
});
it('handles an invalid hair path gracefully', async () => {
+1 -6
View File
@@ -1,12 +1,7 @@
import { STRING_ERROR_MSG, STRING_DOES_NOT_EXIST_MSG } from '../helpers/content.helper';
import { STRING_DOES_NOT_EXIST_MSG } from '../helpers/content.helper';
import translator from '../../website/common/script/content/translation';
describe('Translator', () => {
it('returns error message if string is not properly formatted', () => {
const improperlyFormattedString = translator('petName', { attr: 0 })();
expect(improperlyFormattedString).to.match(STRING_ERROR_MSG);
});
it('returns an error message if string does not exist', () => {
const stringDoesNotExist = translator('stringDoesNotExist')();
expect(stringDoesNotExist).to.match(STRING_DOES_NOT_EXIST_MSG);
+2 -2
View File
@@ -1,8 +1,8 @@
import i18n from '../../website/common/script/i18n';
import './globals.helper';
import { translations } from '../../website/server/libs/i18n';
import { contentTranslations } from '../../website/server/libs/i18n';
i18n.translations = translations;
i18n.translations = contentTranslations;
export const STRING_ERROR_MSG = /^Error processing the string ".*". Please see Help > Report a Bug.$/;
export const STRING_DOES_NOT_EXIST_MSG = /^String '.*' not found.$/;
+1
View File
@@ -21,6 +21,7 @@ export async function getProperty (collectionName, id, path) {
// Specifically helpful for the GET /groups tests,
// resets the db to an empty state and creates a tavern document
export async function resetHabiticaDB () {
console.info('Resetting Habitica DB');
const groups = mongoose.connection.db.collection('groups');
const users = mongoose.connection.db.collection('users');
return mongoose.connection.dropDatabase()
+1 -1
View File
@@ -32,6 +32,6 @@
<script type="text/javascript" src="//cloudfront.loggly.com/js/loggly.tracker-latest.min.js" async></script>
<!-- Translations -->
<script type='text/javascript' src='/api/v4/i18n/browser-script' vite-ignore></script>
<script type='text/javascript' src='/api/v4/i18n/core' vite-ignore></script>
</body>
</html>
+4968 -2726
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -4,6 +4,7 @@
"private": true,
"scripts": {
"serve": "vite",
"serve:docker": "npx vite --host 0.0.0.0",
"build": "vite build",
"preview": "vite preview",
"test:unit": "vitest run",
@@ -33,6 +34,7 @@
"jquery": "^3.7.1",
"lodash": "^4.17.21",
"markdown-it": "^14.0.0",
"micromustache": "^8.0.3",
"moment": "^2.29.4",
"nconf": "^0.12.1",
"sass": "^1.63.4",
@@ -1060,6 +1060,11 @@
width: 141px;
height: 147px;
}
.background_elven_citadel {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_elven_citadel.png');
width: 141px;
height: 147px;
}
.background_enchanted_music_room {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_enchanted_music_room.png');
width: 141px;
@@ -1931,6 +1936,11 @@
width: 141px;
height: 147px;
}
.background_riding_a_comet {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_riding_a_comet.png');
width: 141px;
height: 147px;
}
.background_rime_ice {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_rime_ice.png');
width: 141px;
@@ -2427,6 +2437,11 @@
width: 141px;
height: 147px;
}
.background_waterfall_with_rainbow {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_waterfall_with_rainbow.png');
width: 141px;
height: 147px;
}
.background_wedding_arch {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_wedding_arch.png');
width: 141px;
@@ -29800,6 +29815,11 @@
width: 114px;
height: 90px;
}
.broad_armor_armoire_handstandOutfit {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_armoire_handstandOutfit.png');
width: 114px;
height: 90px;
}
.broad_armor_armoire_hattersSuit {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_armoire_hattersSuit.png');
width: 114px;
@@ -30075,6 +30095,11 @@
width: 114px;
height: 90px;
}
.broad_armor_armoire_softYellowSuit {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_armoire_softYellowSuit.png');
width: 114px;
height: 90px;
}
.broad_armor_armoire_springPetalYukata {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_armoire_springPetalYukata.png');
width: 114px;
@@ -30385,6 +30410,11 @@
width: 114px;
height: 90px;
}
.head_armoire_floppyYellowHat {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_floppyYellowHat.png');
width: 114px;
height: 90px;
}
.head_armoire_flutteryWig {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_flutteryWig.png');
width: 114px;
@@ -30705,6 +30735,11 @@
width: 114px;
height: 90px;
}
.head_armoire_verdantArmingCap {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_verdantArmingCap.png');
width: 114px;
height: 90px;
}
.head_armoire_vermilionArcherHelm {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_vermilionArcherHelm.png');
width: 90px;
@@ -31120,6 +31155,11 @@
width: 114px;
height: 90px;
}
.shield_armoire_softYellowPillow {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_armoire_softYellowPillow.png');
width: 114px;
height: 90px;
}
.shield_armoire_spanishGuitar {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_armoire_spanishGuitar.png');
width: 114px;
@@ -31170,6 +31210,11 @@
width: 114px;
height: 90px;
}
.shield_armoire_verdantBanner {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_armoire_verdantBanner.png');
width: 114px;
height: 90px;
}
.shield_armoire_vikingShield {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_armoire_vikingShield.png');
width: 90px;
@@ -31440,6 +31485,11 @@
width: 114px;
height: 90px;
}
.slim_armor_armoire_handstandOutfit {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_handstandOutfit .png');
width: 114px;
height: 90px;
}
.slim_armor_armoire_hattersSuit {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_hattersSuit.png');
width: 114px;
@@ -31715,6 +31765,11 @@
width: 114px;
height: 90px;
}
.slim_armor_armoire_softYellowSuit {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_softYellowSuit.png');
width: 114px;
height: 90px;
}
.slim_armor_armoire_springPetalYukata {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_springPetalYukata.png');
width: 114px;
@@ -34125,11 +34180,21 @@
width: 114px;
height: 90px;
}
.back_mystery_202605 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/back_mystery_202605.png');
width: 114px;
height: 90px;
}
.broad_armor_mystery_202512 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_mystery_202512.png');
width: 114px;
height: 90px;
}
.broad_armor_mystery_202604 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_mystery_202604.png');
width: 114px;
height: 90px;
}
.head_mystery_202512 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_mystery_202512.png');
width: 114px;
@@ -34140,11 +34205,31 @@
width: 114px;
height: 90px;
}
.head_mystery_202603 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_mystery_202603.png');
width: 114px;
height: 90px;
}
.head_mystery_202604 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_mystery_202604.png');
width: 114px;
height: 90px;
}
.shield_mystery_202605 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_mystery_202605.png');
width: 114px;
height: 90px;
}
.slim_armor_mystery_202512 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_mystery_202512.png');
width: 114px;
height: 90px;
}
.slim_armor_mystery_202604 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_mystery_202604.png');
width: 114px;
height: 90px;
}
.weapon_mystery_202512 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_mystery_202512.png');
width: 114px;
@@ -34155,6 +34240,11 @@
width: 114px;
height: 90px;
}
.weapon_mystery_202603 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_mystery_202603.png');
width: 114px;
height: 90px;
}
.back_mystery_201402 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/back_mystery_201402.png');
width: 90px;
@@ -36275,6 +36365,26 @@
width: 114px;
height: 90px;
}
.broad_armor_special_spring2026Healer {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_special_spring2026Healer.png');
width: 114px;
height: 90px;
}
.broad_armor_special_spring2026Mage {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_special_spring2026Mage.png');
width: 114px;
height: 90px;
}
.broad_armor_special_spring2026Rogue {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_special_spring2026Rogue.png');
width: 114px;
height: 90px;
}
.broad_armor_special_spring2026Warrior {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_special_spring2026Warrior.png');
width: 114px;
height: 90px;
}
.broad_armor_special_springHealer {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_special_springHealer.png');
width: 90px;
@@ -36595,6 +36705,26 @@
width: 114px;
height: 90px;
}
.head_special_spring2026Healer {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_special_spring2026Healer.png');
width: 114px;
height: 90px;
}
.head_special_spring2026Mage {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_special_spring2026Mage.png');
width: 114px;
height: 90px;
}
.head_special_spring2026Rogue {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_special_spring2026Rogue.png');
width: 114px;
height: 90px;
}
.head_special_spring2026Warrior {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_special_spring2026Warrior.png');
width: 114px;
height: 90px;
}
.head_special_springHealer {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_special_springHealer.png');
width: 90px;
@@ -36780,6 +36910,21 @@
width: 114px;
height: 90px;
}
.shield_special_spring2026Healer {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_special_spring2026Healer.png');
width: 114px;
height: 90px;
}
.shield_special_spring2026Rogue {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_special_spring2026Rogue.png');
width: 114px;
height: 90px;
}
.shield_special_spring2026Warrior {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_special_spring2026Warrior.png');
width: 114px;
height: 90px;
}
.shield_special_springHealer {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_special_springHealer.png');
width: 90px;
@@ -37015,6 +37160,26 @@
width: 114px;
height: 90px;
}
.slim_armor_special_spring2026Healer {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_special_spring2026Healer.png');
width: 114px;
height: 90px;
}
.slim_armor_special_spring2026Mage {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_special_spring2026Mage.png');
width: 114px;
height: 90px;
}
.slim_armor_special_spring2026Rogue {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_special_spring2026Rogue.png');
width: 114px;
height: 90px;
}
.slim_armor_special_spring2026Warrior {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_special_spring2026Warrior.png');
width: 114px;
height: 90px;
}
.slim_armor_special_springHealer {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_special_springHealer.png');
width: 90px;
@@ -37255,6 +37420,26 @@
width: 114px;
height: 90px;
}
.weapon_special_spring2026Healer {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_special_spring2026Healer.png');
width: 114px;
height: 90px;
}
.weapon_special_spring2026Mage {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_special_spring2026Mage.png');
width: 114px;
height: 90px;
}
.weapon_special_spring2026Rogue {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_special_spring2026Rogue.png');
width: 114px;
height: 90px;
}
.weapon_special_spring2026Warrior {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_special_spring2026Warrior.png');
width: 114px;
height: 90px;
}
.weapon_special_springHealer {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_special_springHealer.png');
width: 90px;
File diff suppressed because it is too large Load Diff
@@ -311,7 +311,7 @@
<input
id="passwordInput"
v-model="password"
class="form-control input-with-error"
class="form-control dark input-with-error"
type="password"
:placeholder="$t('password')"
:class="{'input-invalid': passwordInvalid, 'input-valid': passwordValid}"
@@ -323,7 +323,7 @@
{{ $t('minPasswordLength') }}
</div>
</div>
<div class="form-group">
<div class="form-group mb-4">
<label
v-once
for="confirmPasswordInput"
@@ -331,7 +331,7 @@
<input
id="confirmPasswordInput"
v-model="passwordConfirm"
class="form-control input-with-error"
class="form-control dark input-with-error"
type="password"
:placeholder="$t('confirmPasswordPlaceholder')"
:class="{'input-invalid': passwordConfirmInvalid, 'input-valid': passwordConfirmValid}"
@@ -344,13 +344,14 @@
</div>
</div>
<div class="text-center">
<div
class="btn btn-info"
:enabled="!resetPasswordSetNewOneData.hasError"
<button
class="btn btn-info w-100"
:disabled="!password || !passwordConfirm
|| password !== passwordConfirm || resetPasswordSetNewOneData.hasError"
@click="resetPasswordSetNewOneLink()"
>
{{ $t('setNewPass') }}
</div>
</button>
</div>
</form>
</div>
@@ -1,8 +1,8 @@
<template>
<div
class="banner d-flex align-items-center justify-content-between py-3 px-4"
id="privacy-banner"
v-if="!hidden"
id="privacy-banner"
class="banner d-flex align-items-center justify-content-between py-3 px-4"
>
<p
class="mr-3 mb-0"
@@ -34,7 +34,7 @@
>
<div
class="close-x"
@click="remove()"
@click.stop="remove()"
>
<div
class="svg-icon svg-close"
@@ -140,7 +140,7 @@ export default {
methods: {
remove () {
if (this.eventKey) {
window.sessionStorage.setItem(`hide-g1g1-${this.eventKey}`, 'true');
window.localStorage.setItem(`hide-g1g1-${this.eventKey}`, 'true');
}
this.$emit('notification-removed');
},
@@ -318,7 +318,7 @@ export default {
shouldShowG1g1 () {
if (!this.currentG1g1Event) return false;
const eventKey = this.g1g1EventKey;
if (eventKey && window.sessionStorage.getItem(`hide-g1g1-${eventKey}`) === 'true') {
if (eventKey && window.localStorage.getItem(`hide-g1g1-${eventKey}`) === 'true') {
return false;
}
return !this.g1g1Hidden;
@@ -105,7 +105,7 @@ export default {
},
data () {
return {
privacyConsent: true,
privacyConsent: false,
};
},
methods: {
@@ -281,6 +281,11 @@
.badge-dialog {
left: -8px;
top: -8px;
.badge-pin {
width: 32px;
height: 32px;
}
}
.avatar {
@@ -851,10 +856,17 @@ export default {
- ownedMounts
- ownedItems;
if (
petsRemaining < 0
&& !window.confirm(this.$t('purchasePetItemConfirm', { itemText: this.item.text })) // eslint-disable-line no-alert
) return;
if (petsRemaining < 0) {
const confirmed = await new Promise(resolve => {
this.$root.$emit('habitica:purchase-confirm', {
message: this.$t('purchasePetItemConfirm', { itemText: this.item.text }),
currency: this.item.currency,
cost: this.item.value * this.selectedAmountToBuy,
resolve,
});
});
if (!confirmed) return;
}
}
if (this.item.purchaseType === 'customization') {
@@ -866,11 +878,14 @@ export default {
this.purchased(this.item.text);
} else {
const shouldConfirmPurchase = this.item.currency === 'gems' || this.item.currency === 'hourglasses';
if (
shouldConfirmPurchase
&& !this.confirmPurchase(this.item.currency, this.item.value * this.selectedAmountToBuy)
) {
return;
if (shouldConfirmPurchase) {
const confirmed = await this.confirmPurchase(
this.item.currency,
this.item.value * this.selectedAmountToBuy,
);
if (!confirmed) {
return;
}
}
if (this.genericPurchase) {
if (this.item.key === 'rebirth_orb') {
@@ -893,8 +908,8 @@ export default {
purchaseGems () {
this.$root.$emit('bv::show::modal', 'buy-gems');
},
togglePinned () {
this.isPinned = this.$store.dispatch('user:togglePinnedItem', { type: this.item.pinType, path: this.item.path });
async togglePinned () {
this.isPinned = await this.$store.dispatch('user:togglePinnedItem', { type: this.item.pinType, path: this.item.path });
if (!this.isPinned) {
this.text(this.$t('unpinnedItem', { item: this.item.text }));
@@ -76,7 +76,21 @@
:empty-item="false"
:show-popover="Boolean(ctx.item.text)"
@click="selectItem(ctx.item)"
/>
>
<template
slot="itemBadge"
slot-scope="slotProps"
>
<span
class="badge-top"
@click.prevent.stop="togglePinned(slotProps.item)"
>
<pin-badge
:pinned="slotProps.item.pinned"
/>
</span>
</template>
</shop-item>
</template>
</item-rows>
</div>
@@ -108,6 +122,16 @@
}
</style>
<style lang="scss">
.market .badge-pin:not(.pinned) {
display: none;
}
.market .item:hover .badge-pin {
display: block;
}
</style>
<script>
import find from 'lodash/find';
import shops from '@/../../common/script/libs/shops';
@@ -118,7 +142,9 @@ import Checkbox from '@/components/ui/checkbox';
import FilterGroup from '@/components/ui/filterGroup';
import FilterSidebar from '@/components/ui/filterSidebar';
import ItemRows from '@/components/ui/itemRows';
import PinBadge from '@/components/ui/pinBadge';
import ShopItem from '../shopItem';
import pinUtils from '@/mixins/pinUtils';
export default {
components: {
@@ -126,8 +152,10 @@ export default {
FilterGroup,
FilterSidebar,
ItemRows,
PinBadge,
ShopItem,
},
mixins: [pinUtils],
data () {
return {
searchText: null,
@@ -184,8 +212,12 @@ export default {
methods: {
customizationsItems (options = {}) {
const { category, searchBy } = options;
return category.items.filter(item => !searchBy
|| item.text.toLowerCase().includes(searchBy));
return category.items
.filter(item => !searchBy || item.text.toLowerCase().includes(searchBy))
.map(item => ({
...item,
pinned: this.isPinned(item),
}));
},
emptyClick (identifier, event) {
if (event.target.tagName !== 'A') return;
@@ -0,0 +1,207 @@
<template>
<b-modal
id="purchase-confirm-modal"
:hide-footer="true"
:hide-header="true"
modal-class="purchase-confirm-modal"
centered
>
<div class="modal-content-wrapper">
<div class="top-bar"></div>
<div class="modal-body-content">
<div
class="currency-chip"
:class="currency"
>
<span
class="svg-icon icon-24"
v-html="icons[currency]"
></span>
<span class="cost-value">{{ cost }}</span>
</div>
<h2 class="modal-title">
{{ $t('confirmPurchase') }}
</h2>
<p class="modal-subtitle">
{{ confirmationMessage }}
</p>
<div class="button-wrapper">
<button
class="btn btn-primary"
@click="confirm()"
>
{{ $t('confirm') }}
</button>
<button
class="btn-cancel"
@click="cancel()"
>
{{ $t('cancel') }}
</button>
</div>
</div>
</div>
</b-modal>
</template>
<script>
import svgGem from '@/assets/svg/gem.svg?raw';
import svgHourglass from '@/assets/svg/hourglass.svg?raw';
export default {
data () {
return {
confirmationMessage: '',
currency: 'gems',
cost: 0,
resolveCallback: null,
icons: Object.freeze({
gems: svgGem,
hourglasses: svgHourglass,
}),
};
},
mounted () {
this.$root.$on('habitica:purchase-confirm', config => {
this.confirmationMessage = config.message;
this.currency = config.currency || 'gems';
this.cost = config.cost || 0;
this.resolveCallback = config.resolve;
this.$root.$emit('bv::show::modal', 'purchase-confirm-modal');
});
},
beforeDestroy () {
this.$root.$off('habitica:purchase-confirm');
},
methods: {
confirm () {
if (this.resolveCallback) {
this.resolveCallback(true);
}
this.close();
},
cancel () {
if (this.resolveCallback) {
this.resolveCallback(false);
}
this.close();
},
close () {
this.$root.$emit('bv::hide::modal', 'purchase-confirm-modal');
},
},
};
</script>
<style lang="scss" scoped>
@import '@/assets/scss/colors.scss';
::v-deep .purchase-confirm-modal {
.modal-dialog {
max-width: 330px;
margin: auto;
}
.modal-content {
border-radius: 8px;
overflow: hidden;
border: none;
}
.modal-body {
padding: 0;
}
}
.modal-content-wrapper {
display: flex;
flex-direction: column;
}
.top-bar {
height: 8px;
background-color: $purple-300;
}
.modal-body-content {
display: flex;
flex-direction: column;
align-items: center;
padding: 0 24px 24px;
}
.currency-chip {
display: flex;
align-items: center;
gap: 8px;
margin-top: 40px;
padding: 8px 20px;
border-radius: 20px;
font-size: 1.25rem;
font-weight: bold;
line-height: 1.4;
&.gems {
color: $gems-color;
background-color: rgba($green-10, 0.15);
}
&.hourglasses {
color: $hourglass-color;
background-color: rgba($blue-10, 0.15);
}
.icon-24 {
width: 24px;
height: 24px;
}
}
.modal-title {
margin-top: 16px;
margin-bottom: 0;
color: $purple-300;
font-family: 'Roboto Condensed', sans-serif;
font-weight: 700;
font-size: 20px;
text-align: center;
}
.modal-subtitle {
margin-top: 12px;
margin-bottom: 0;
font-family: Roboto, sans-serif;
font-weight: 700;
font-size: 14px;
line-height: 24px;
letter-spacing: 0;
text-align: center;
color: $gray-50;
}
.button-wrapper {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
margin-top: 16px;
gap: 8px;
}
.btn-cancel {
background: none;
border: none;
color: $purple-300;
font-family: Roboto, sans-serif;
font-weight: 400;
font-size: 14px;
line-height: 24px;
letter-spacing: 0;
cursor: pointer;
padding: 8px 16px;
&:hover {
text-decoration: underline;
}
}
</style>
@@ -180,6 +180,11 @@
.badge-dialog {
left: -8px;
top: -8px;
.badge-pin {
width: 32px;
height: 32px;
}
}
.modal-content {
@@ -510,8 +515,12 @@ export default {
this.selectedAmountToBuy = 1;
this.$emit('change', $event);
},
buyItem () {
if (!this.confirmPurchase(this.item.currency, this.item.value * this.selectedAmountToBuy)) {
async buyItem () {
const confirmed = await this.confirmPurchase(
this.item.currency,
this.item.value * this.selectedAmountToBuy,
);
if (!confirmed) {
return;
}
this.makeGenericPurchase(this.item, 'buyQuestModal', this.selectedAmountToBuy);
@@ -498,8 +498,13 @@ export default {
await this.triggerGetWorldState();
this.currentEvent = _find(this.currentEventList, event => Boolean(event.season));
this.imageURLs.background = `url(/static/npc/${this.currentEvent.season}/seasonal_shop_opened_background.png)`;
this.imageURLs.npc = `url(/static/npc/${this.currentEvent.season}/seasonal_shop_opened_npc.png)`;
if (this.currentEvent.season === 'valentines') {
this.imageURLs.background = 'url(/static/npc/spring/seasonal_shop_opened_background.png)';
this.imageURLs.npc = 'url(/static/npc/spring/seasonal_shop_opened_npc.png)';
} else {
this.imageURLs.background = `url(/static/npc/${this.currentEvent.season}/seasonal_shop_opened_background.png)`;
this.imageURLs.npc = `url(/static/npc/${this.currentEvent.season}/seasonal_shop_opened_npc.png)`;
}
},
beforeDestroy () {
this.$root.$off('buyModal::boughtItem');
@@ -64,9 +64,11 @@
<li>sexual orientation; and</li>
<li>information collected from a known child.</li>
</ul>
<p><strong>
NOTE: Please do not provide us sensitive personal information or sensitive personal data, as those terms are defined under applicable privacy laws, unless we directly request that you do so. If you feel, after careful consideration, that it is necessary to provide us certain sensitive personal information or data, please provide us the minimum amount of such information or data that is necessary.
</strong></p>
<p>
<strong>
NOTE: Please do not provide us sensitive personal information or sensitive personal data, as those terms are defined under applicable privacy laws, unless we directly request that you do so. If you feel, after careful consideration, that it is necessary to provide us certain sensitive personal information or data, please provide us the minimum amount of such information or data that is necessary.
</strong>
</p>
<h3 id="section_1_1">
1.1 Information You Provide Directly
</h3>
@@ -617,7 +619,7 @@
7. General Audience Services
</h2>
<p>
The Service is intended for users 18 years or older; you are not permitted to access or use the Service if you are younger than 18. We do not knowingly collect personal information from children under the age of 18 through the Service. We encourage parents and legal guardians to monitor their childrens Internet usage and to help enforce our Privacy Policy by instructing their children to never provide personal information without their permission. If you have reason to believe that a child under the age of 18 has provided personal information to us, please contact us at <a href='mailto:privacy@habitica.com'>privacy@habitica.com</a>, and we will delete that information from our databases.
The Service is intended for users 18 years or older; you are not permitted to access or use the Service if you are younger than 18. We do not knowingly collect personal information from children under the age of 18 through the Service. We encourage parents and legal guardians to monitor their childrens Internet usage and to help enforce our Privacy Policy by instructing their children to never provide personal information without their permission. If you have reason to believe that a child under the age of 18 has provided personal information to us, please contact us at <a href="mailto:privacy@habitica.com">privacy@habitica.com</a>, and we will delete that information from our databases.
</p>
<h2 id="section_8">
@@ -708,7 +710,7 @@
<p><strong><u>Nevada Residents</u></strong></p>
<p>
Nevada residents may opt out of the sale of certain “covered information” collected by operators of websites or online services. We currently do not sell covered information, as “sale” is defined by such law, and do not have plans to do so. In accordance with Nevada law, you may submit to us a verified request instructing us not to sell your covered information by sending an email to <a href='mailto:privacy@habitica.com'>privacy@habitica.com</a>.
Nevada residents may opt out of the sale of certain “covered information” collected by operators of websites or online services. We currently do not sell covered information, as “sale” is defined by such law, and do not have plans to do so. In accordance with Nevada law, you may submit to us a verified request instructing us not to sell your covered information by sending an email to <a href="mailto:privacy@habitica.com">privacy@habitica.com</a>.
</p>
<p><strong><u>Notice to United Kingdom/European/Switzerland Residents.</u></strong></p>
<p>
@@ -15,8 +15,8 @@
<router-view />
</div>
<div
id="bottom-background"
v-if="loginFlow"
id="bottom-background"
class="bg-purple-300"
>
<div class="seamless_mountains_demo_repeat"></div>
@@ -31,7 +31,10 @@
id="bottom-wrap"
class="purple-4"
>
<div id="bottom-background" v-if="!loginFlow">
<div
v-if="!loginFlow"
id="bottom-background"
>
<div class="seamless_mountains_demo_repeat"></div>
<div class="midground_foreground_extended2"></div>
</div>
@@ -104,9 +107,10 @@
footer, footer a {
background: transparent;
color: $purple-500;
&:hover {
color: $white;
}
}
footer a:hover {
color: $white;
}
h3 {
@@ -117,10 +121,6 @@
border-top-color: $purple-100;
}
.donate-text {
color: $purple-500;
}
.logo {
color: $purple-300;
}
@@ -129,42 +129,27 @@
color: $purple-500;
}
.social .d-flex:hover {
a {
color: $white;
}
svg {
fill: $white;
}
}
.social-circle {
background: $purple-50;
color: $purple-500;
.instagram svg {
svg {
background-color: $purple-50;
fill: $purple-500;
&:hover {
fill: $white;
}
}
.bluesky svg {
background-color: $purple-50;
fill: $purple-500;
&:hover {
fill: $white;
}
}
.facebook svg {
background-color: $purple-50;
fill: $purple-500;
&:hover {
fill: $white;
}
}
.tumblr svg {
background-color: $purple-50;
fill: $purple-500;
&:hover {
fill: $white;
}
width: 24px;
height: 24px;
}
}
.btn-contribute {
background: $white;
box-shadow: none;
@@ -274,7 +259,8 @@ export default {
return 'purple-footer';
},
loginFlow () {
return ['login', 'register', 'username'].indexOf(this.$route.name) !== -1;
const loginRoutes = ['forgotPassword', 'login', 'register', 'resetPassword', 'username'];
return loginRoutes.indexOf(this.$route.name) !== -1;
},
showContentWrap () {
return this.$route.name !== 'news';
@@ -158,7 +158,7 @@
BY PURCHASING PREMIUM YOU EXPRESSLY UNDERSTAND AND AGREE TO OUR REFUND POLICY:
</p>
<p>
YOU CAN REQUEST A REFUND OF YOUR MOST RECENT PAYMENT TO US BY CONTACTING US AT <a href='mailto:admin@habitica.com'>ADMIN@HABITICA.COM</a>. THE AMOUNT OF YOUR REFUND, IF ANY, WILL BE BASED ON (1) THE AMOUNT OF YOUR PURCHASED BUT UNUSED SUBSCRIPTION BENEFITS AND (2) THE TERMS IMPOSED ON US BY OUR PAYMENT PROCESSING VENDORS (E.G., WITH RESPECT TO THE DURATION OF THE REFUND PERIOD).
YOU CAN REQUEST A REFUND OF YOUR MOST RECENT PAYMENT TO US BY CONTACTING US AT <a href="mailto:admin@habitica.com">ADMIN@HABITICA.COM</a>. THE AMOUNT OF YOUR REFUND, IF ANY, WILL BE BASED ON (1) THE AMOUNT OF YOUR PURCHASED BUT UNUSED SUBSCRIPTION BENEFITS AND (2) THE TERMS IMPOSED ON US BY OUR PAYMENT PROCESSING VENDORS (E.G., WITH RESPECT TO THE DURATION OF THE REFUND PERIOD).
</p>
<p>
FOR ANY CUSTOMER WHO PURCHASED PREMIUM IN APPLE INC.'s APP STORE ("APP STORE"), PLEASE CONTACT APPLE INC.'s SUPPORT TEAM: <a
@@ -1,65 +1,45 @@
<template>
<b-modal
id="broken-task-modal"
title="Broken Challenge"
size="sm"
:hide-footer="true"
:hide-header="true"
modal-class="broken-task-confirm-modal"
centered
>
<div
v-if="brokenChallengeTask && brokenChallengeTask.challenge"
class="modal-body"
class="modal-content-wrapper"
>
<div
v-if="brokenChallengeTask.challenge.broken === 'TASK_DELETED'
|| brokenChallengeTask.challenge.broken === 'CHALLENGE_TASK_NOT_FOUND'"
>
<h2>{{ $t('brokenTask') }}</h2>
<div>
<div class="top-bar"></div>
<div class="modal-body-content">
<div
class="icon-wrapper"
v-html="icons.alertIcon"
></div>
<h2 class="modal-title">
{{ modalTitle }}
</h2>
<p class="modal-subtitle">
{{ modalSubtitle }}
</p>
<div class="button-wrapper">
<button
class="btn btn-primary"
@click="unlink('keep')"
@click="keepAction()"
>
{{ $t('keepIt') }}
{{ keepButtonText }}
</button>
<button
class="btn btn-danger"
@click="removeTask(obj)"
@click="removeAction()"
>
{{ $t('removeIt') }}
</button>
</div>
</div>
<div v-if="brokenChallengeTask.challenge.broken === 'CHALLENGE_DELETED'">
<h2>{{ $t('brokenChallenge') }}</h2>
<div>
<button
class="btn btn-primary"
@click="unlink('keep-all')"
>
{{ $t('keepTasks') }}
{{ removeButtonText }}
</button>
<button
class="btn btn-danger"
@click="unlink('remove-all')"
class="btn-cancel"
@click="close()"
>
{{ $t('removeTasks') }}
</button>
</div>
</div>
<div v-if="brokenChallengeTask.challenge.broken === 'CHALLENGE_CLOSED'">
<h2 v-html="$t('challengeCompleted', {user: brokenChallengeTask.challenge.winner})"></h2>
<div>
<button
class="btn btn-primary"
@click="unlink('keep-all')"
>
{{ $t('keepTasks') }}
</button>
<button
class="btn btn-danger"
@click="unlink('remove-all')"
>
{{ $t('removeTasks') }}
{{ $t('cancel') }}
</button>
</div>
</div>
@@ -67,23 +47,175 @@
</b-modal>
</template>
<style scoped>
.modal-body {
padding-bottom: 2em;
<style lang="scss" scoped>
@import '@/assets/scss/colors.scss';
::v-deep .broken-task-confirm-modal {
.modal-dialog {
max-width: 330px;
margin: auto;
}
.modal-content {
border-radius: 8px;
overflow: hidden;
border: none;
}
.modal-body {
padding: 0;
}
}
.modal-content-wrapper {
display: flex;
flex-direction: column;
}
.top-bar {
height: 8px;
background-color: $maroon-100;
}
.modal-body-content {
display: flex;
flex-direction: column;
align-items: center;
padding: 0 24px 24px;
}
.icon-wrapper {
margin-top: 40px;
width: 48px;
height: 48px;
::v-deep svg {
width: 48px;
height: 48px;
path {
fill: #DE3F3F;
}
}
}
.modal-title {
margin-top: 16px;
margin-bottom: 0;
color: $maroon-100;
font-family: 'Roboto Condensed', sans-serif;
font-weight: 700;
font-size: 20px;
text-align: center;
}
.modal-subtitle {
margin-top: 12px;
margin-bottom: 0;
font-family: Roboto, sans-serif;
font-weight: 400;
font-size: 14px;
line-height: 24px;
letter-spacing: 0;
text-align: center;
color: $gray-50;
}
.button-wrapper {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
margin-top: 16px;
gap: 8px;
}
.btn-cancel {
background: none;
border: none;
color: $purple-300;
font-family: Roboto, sans-serif;
font-weight: 400;
font-size: 14px;
line-height: 24px;
letter-spacing: 0;
cursor: pointer;
padding: 8px 16px;
&:hover {
text-decoration: underline;
}
}
</style>
<script>
import { mapActions } from '@/libs/store';
import notifications from '@/mixins/notifications';
import alertIcon from '@/assets/svg/for-css/alert.svg?raw';
export default {
mixins: [notifications],
data () {
return {
brokenChallengeTask: {},
icons: Object.freeze({
alertIcon,
}),
};
},
computed: {
brokenType () {
return this.brokenChallengeTask.challenge?.broken;
},
isSingleTask () {
return this.brokenType === 'TASK_DELETED'
|| this.brokenType === 'CHALLENGE_TASK_NOT_FOUND';
},
brokenChallengeTaskCount () {
if (!this.brokenChallengeTask.challenge?.id) return 0;
const challengeId = this.brokenChallengeTask.challenge.id;
const tasksData = this.$store.state.tasks.data;
let count = 0;
['habits', 'dailys', 'todos', 'rewards'].forEach(type => {
if (tasksData[type]) {
count += tasksData[type].filter(
t => t.challenge && t.challenge.id === challengeId,
).length;
}
});
return count;
},
modalTitle () {
if (this.isSingleTask) {
return this.$t('brokenTask');
}
if (this.brokenType === 'CHALLENGE_CLOSED') {
return this.$t('challengeCompleted');
}
return this.$t('brokenChallenge');
},
modalSubtitle () {
if (this.isSingleTask) {
return this.$t('brokenTaskDescription');
}
if (this.brokenType === 'CHALLENGE_CLOSED') {
return this.$t('challengeCompletedDescription', { user: this.brokenChallengeTask.challenge?.winner });
}
return this.$t('brokenChallengeDescription');
},
keepButtonText () {
if (this.isSingleTask) {
return this.$t('keepIt');
}
return this.$t('keepTasks');
},
removeButtonText () {
if (this.isSingleTask) {
return this.$t('removeIt');
}
return this.$t('removeTasks');
},
},
mounted () {
this.$root.$on('handle-broken-task', task => {
this.brokenChallengeTask = { ...task };
@@ -99,8 +231,36 @@ export default {
unlinkOneTask: 'tasks:unlinkOneTask',
unlinkAllTasks: 'tasks:unlinkAllTasks',
}),
keepAction () {
if (this.isSingleTask) {
this.unlink('keep');
} else {
this.unlink('keep-all');
}
},
async removeAction () {
if (this.isSingleTask) {
await this.removeTask();
} else {
await this.unlink('remove-all');
}
},
async unlink (keepOption) {
if (keepOption.indexOf('-all') !== -1) {
if (keepOption === 'remove-all') {
const count = this.brokenChallengeTaskCount;
const confirmed = await new Promise(resolve => {
this.$root.$emit('habitica:delete-task-confirm', {
title: count === 1 ? this.$t('deleteTask') : this.$t('deleteXTasks', { count }),
description: this.$t('brokenChallengeTaskCount', { count }),
message: this.$t('confirmDeleteTasks'),
buttonText: count === 1 ? this.$t('deleteTask') : this.$t('deleteXTasks', { count }),
resolve,
});
});
if (!confirmed) return;
}
await this.unlinkAllTasks({
challengeId: this.brokenChallengeTask.challenge.id,
keep: keepOption,
@@ -122,8 +282,14 @@ export default {
});
this.close();
},
removeTask () {
if (!window.confirm('Are you sure you want to delete this task?')) return; // eslint-disable-line no-alert
async removeTask () {
const confirmed = await new Promise(resolve => {
this.$root.$emit('habitica:delete-task-confirm', {
message: this.$t('sureDelete'),
resolve,
});
});
if (!confirmed) return;
this.destroyTask(this.brokenChallengeTask);
this.close();
},
+4 -20
View File
@@ -87,7 +87,7 @@
ref="tasksList"
class="sortable-tasks"
:disabled="activeFilter.label === 'scheduled' || !canBeDragged()"
scrollSensitivity="64"
scroll-sensitivity="64"
:delay-on-touch-only="true"
:delay="100"
@update="taskSorted"
@@ -348,7 +348,6 @@
import throttle from 'lodash/throttle';
import isEmpty from 'lodash/isEmpty';
import draggable from 'vuedraggable';
import { shouldDo } from '@/../../common/script/cron';
import inAppRewards from '@/../../common/script/libs/inAppRewards';
import taskDefaults from '@/../../common/script/libs/taskDefaults';
import Task from './task';
@@ -482,25 +481,10 @@ export default {
return this.$t('addATask', { type });
},
badgeCount () {
// 0 means the badge will not be shown
// It is shown for the all and due views of dailies
// and for the active and scheduled views of todos.
if (this.type === 'todo' && this.activeFilter.label !== 'complete2') {
return this.taskList.length;
} if (this.type === 'daily') {
if (this.activeFilter.label === 'due') {
return this.taskList.length;
} if (this.activeFilter.label === 'all') {
return this.taskList
.reduce(
(count, t) => (!t.completed
&& shouldDo(new Date(), t, this.getUserPreferences) ? count + 1 : count),
0,
);
}
if (this.type === 'reward') {
return 0;
}
return 0;
return this.taskList.length;
},
},
watch: {
@@ -0,0 +1,223 @@
<template>
<b-modal
id="delete-task-confirm-modal"
:hide-footer="true"
:hide-header="true"
modal-class="delete-confirm-modal"
centered
>
<div class="modal-content-wrapper">
<div class="top-bar"></div>
<div class="modal-body-content">
<div
class="icon-wrapper"
v-html="icons.alertIcon"
></div>
<h2 class="modal-title">
{{ displayTitle }}
</h2>
<p
v-if="description"
class="modal-description"
>
{{ description }}
</p>
<p class="modal-subtitle">
{{ confirmationMessage }}
</p>
<div class="button-wrapper">
<button
class="btn btn-danger"
@click="confirm()"
>
{{ buttonText }}
</button>
<button
class="btn-cancel"
@click="cancel()"
>
{{ $t('cancel') }}
</button>
</div>
</div>
</div>
</b-modal>
</template>
<script>
import alertIcon from '@/assets/svg/for-css/alert.svg?raw';
export default {
data () {
return {
confirmationMessage: '',
taskType: '',
description: '',
customTitle: '',
customButtonText: '',
resolveCallback: null,
icons: Object.freeze({
alertIcon,
}),
};
},
computed: {
displayTitle () {
if (this.customTitle) return this.customTitle;
return this.$t('deleteType', { type: this.taskType });
},
buttonText () {
if (this.customButtonText) return this.customButtonText;
return this.displayTitle;
},
},
mounted () {
this.$root.$on('habitica:delete-task-confirm', config => {
this.confirmationMessage = config.message;
this.taskType = config.taskType || '';
this.description = config.description || '';
this.customTitle = config.title || '';
this.customButtonText = config.buttonText || '';
this.resolveCallback = config.resolve;
this.$root.$emit('bv::show::modal', 'delete-task-confirm-modal');
});
},
beforeDestroy () {
this.$root.$off('habitica:delete-task-confirm');
},
methods: {
confirm () {
if (this.resolveCallback) {
this.resolveCallback(true);
}
this.close();
},
cancel () {
if (this.resolveCallback) {
this.resolveCallback(false);
}
this.close();
},
close () {
this.$root.$emit('bv::hide::modal', 'delete-task-confirm-modal');
},
},
};
</script>
<style lang="scss" scoped>
@import '@/assets/scss/colors.scss';
::v-deep .delete-confirm-modal {
.modal-dialog {
max-width: 330px;
margin: auto;
}
.modal-content {
border-radius: 8px;
overflow: hidden;
border: none;
}
.modal-body {
padding: 0;
}
}
.modal-content-wrapper {
display: flex;
flex-direction: column;
}
.top-bar {
height: 8px;
background-color: $maroon-100;
}
.modal-body-content {
display: flex;
flex-direction: column;
align-items: center;
padding: 0 24px 24px;
}
.icon-wrapper {
margin-top: 40px;
width: 48px;
height: 48px;
::v-deep svg {
width: 48px;
height: 48px;
path {
fill: #DE3F3F;
}
}
}
.modal-title {
margin-top: 16px;
margin-bottom: 0;
color: $maroon-100;
font-family: 'Roboto Condensed', sans-serif;
font-weight: 700;
font-size: 20px;
text-align: center;
}
.modal-description {
margin-top: 12px;
margin-bottom: 0;
font-family: Roboto, sans-serif;
font-weight: 400;
font-size: 14px;
line-height: 24px;
letter-spacing: 0;
text-align: center;
color: $gray-50;
}
.modal-description + .modal-subtitle {
margin-top: 16px;
}
.modal-subtitle {
margin-top: 12px;
margin-bottom: 0;
font-family: Roboto, sans-serif;
font-weight: 700;
font-size: 14px;
line-height: 24px;
letter-spacing: 0;
text-align: center;
color: $gray-50;
}
.button-wrapper {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
margin-top: 16px;
gap: 8px;
}
.btn-cancel {
background: none;
border: none;
color: $purple-300;
font-family: Roboto, sans-serif;
font-weight: 400;
font-size: 14px;
line-height: 24px;
letter-spacing: 0;
cursor: pointer;
padding: 8px 16px;
&:hover {
text-decoration: underline;
}
}
</style>
+9 -2
View File
@@ -1177,9 +1177,16 @@ export default {
moveToBottom () {
this.$emit('moveTo', this.task, 'bottom');
},
destroy () {
async destroy () {
const type = this.$t(this.task.type);
if (!window.confirm(this.$t('sureDeleteType', { type }))) return; // eslint-disable-line no-alert
const confirmed = await new Promise(resolve => {
this.$root.$emit('habitica:delete-task-confirm', {
message: this.$t('sureDeleteType', { type }),
taskType: type,
resolve,
});
});
if (!confirmed) return;
this.destroyTask(this.task);
this.$emit('taskDestroyed', this.task);
},
@@ -150,14 +150,14 @@
<button
type="button"
class="habit-option-container no-transition
d-flex flex-column justify-content-center align-items-center"
d-flex flex-column justify-content-center align-items-center"
:class="!task.up ? cssClass('habit-control-disabled') : ''"
:disabled="challengeAccessRequired"
@click="toggleUpDirection()"
>
<div
class="habit-option-button no-transition
d-flex justify-content-center align-items-center mb-2"
d-flex justify-content-center align-items-center mb-2"
:class="task.up ? cssClass('bg') : ''"
>
<div
@@ -176,14 +176,14 @@
<button
type="button"
class="habit-option-container no-transition
d-flex flex-column justify-content-center align-items-center"
d-flex flex-column justify-content-center align-items-center"
:class="!task.down ? cssClass('habit-control-disabled') : ''"
:disabled="challengeAccessRequired"
@click="toggleDownDirection()"
>
<div
class="habit-option-button no-transition
d-flex justify-content-center align-items-center mb-2"
d-flex justify-content-center align-items-center mb-2"
:class="task.down ? cssClass('bg') : ''"
>
<div
@@ -382,6 +382,45 @@
</div>
</div>
</div>
<div
v-if="showStatAssignment"
class="stat-assignment option mt-3"
>
<div class="form-group row">
<label
v-once
class="col-12 mb-1"
>{{ $t('assignedStat') }}</label>
<div class="col-12">
<div class="stat-dropdown-container">
<select-list
:items="statOptions"
:value="task.attribute"
key-prop="key"
active-key-prop="key"
@select="task.attribute = $event.key"
>
<template #item="{ item, button }">
<div class="stat-option-content">
<span
class="stat-option-title"
:class="item.key"
>
{{ $t(item.label) }}
</span>
<span
v-if="!button"
class="stat-option-description"
>
{{ $t(item.description) }}
</span>
</div>
</template>
</select-list>
</div>
</div>
</div>
</div>
<div
v-if="task.type === 'habit' && !groupId"
class="option mt-3"
@@ -591,7 +630,7 @@
>
<button
class="btn btn-primary btn-footer
d-flex align-items-center justify-content-center"
d-flex align-items-center justify-content-center"
:class="{'btn-disabled': !canSave}"
type="button"
@click="submit()"
@@ -911,6 +950,87 @@
.streak-addon path {
fill: $gray-200;
}
.stat-dropdown-container {
.select-list {
.selectListItem {
margin-bottom: 0;
&:last-child {
margin-bottom: 0;
}
}
.selectListItem .dropdown-item {
padding: 8px 16px !important;
height: auto !important;
white-space: normal;
word-wrap: break-word;
&:hover,
&:focus {
background-color: rgba($purple-600, 0.25) !important;
}
}
.dropdown-toggle {
display: flex;
align-items: center;
.stat-option-content {
display: flex;
align-items: center;
.stat-option-title {
font-weight: normal;
color: $gray-50;
margin-bottom: 0;
}
}
}
}
.stat-option-content {
display: block;
width: 100%;
.stat-option-title {
display: block;
font-family: Roboto;
font-weight: 700;
font-size: 14px;
line-height: 1.71;
text-transform: capitalize;
margin-bottom: 4px;
&.str {
color: $maroon-100;
}
&.int {
color: $blue-50;
}
&.con {
color: $yellow-5;
}
&.per {
color: $purple-300;
}
}
.stat-option-description {
display: block;
font-family: Roboto;
font-weight: 400;
font-size: 12px;
line-height: 16px;
color: $gray-100;
margin-bottom: 0;
}
}
}
</style>
<style lang="scss" scoped>
@@ -1023,7 +1143,6 @@
.input-group-outer.disabled .input-group-text {
color: $gray-200;
}
</style>
<script>
@@ -1038,6 +1157,7 @@ import SelectMulti from './modal-controls/selectMulti';
import selectDifficulty from '@/components/tasks/modal-controls/selectDifficulty';
import selectTranslatedArray from '@/components/tasks/modal-controls/selectTranslatedArray';
import lockableLabel from '@/components/tasks/modal-controls/lockableLabel';
import selectList from '@/components/ui/selectList';
import syncTask from '../../mixins/syncTask';
@@ -1061,6 +1181,7 @@ export default {
selectTranslatedArray,
toggleCheckbox,
lockableLabel,
selectList,
},
directives: {
markdown: markdownDirective,
@@ -1094,6 +1215,12 @@ export default {
con: 'constitution',
per: 'perception',
},
statOptions: [
{ key: 'str', label: 'strength', description: 'strTaskText' },
{ key: 'int', label: 'intelligence', description: 'intTaskText' },
{ key: 'con', label: 'constitution', description: 'conTaskText' },
{ key: 'per', label: 'perception', description: 'perTaskText' },
],
calendarHighlights: { dates: [new Date()] },
};
},
@@ -1187,6 +1314,12 @@ export default {
selectedTags () {
return this.getTagsFor(this.task);
},
showStatAssignment () {
return this.task.type !== 'reward'
&& !this.groupId
&& this.user.preferences.automaticAllocation === true
&& this.user.preferences.allocationMode === 'taskbased';
},
},
watch: {
task () {
@@ -1305,9 +1438,16 @@ export default {
}
this.$root.$emit('bv::hide::modal', 'task-modal');
},
destroy () {
async destroy () {
const type = this.$t(this.task.type);
if (!window.confirm(this.$t('sureDeleteType', { type }))) return; // eslint-disable-line no-alert
const confirmed = await new Promise(resolve => {
this.$root.$emit('habitica:delete-task-confirm', {
message: this.$t('sureDeleteType', { type }),
taskType: type,
resolve,
});
});
if (!confirmed) return;
this.destroyTask(this.task);
this.$emit('taskDestroyed', this.task);
this.$root.$emit('bv::hide::modal', 'task-modal');
@@ -16,7 +16,7 @@
.badge-pin {
background-color: $white;
color: $gray-200;
color: $gray-100;
transition: none;
display: flex;
cursor: pointer;
@@ -32,8 +32,8 @@
}
.svg-icon {
width: 100%;
height: 100%;
width: 16px;
height: 16px;
}
}
@@ -12,7 +12,7 @@
<template #button-content>
<slot
name="item"
:item="selected || placeholder"
:item="selectedItem || placeholder"
:button="true"
>
<!-- Fallback content -->
@@ -134,6 +134,14 @@ export default {
}),
};
},
computed: {
selectedItem () {
if (this.activeKeyProp) {
return this.items.find(item => item[this.activeKeyProp] === this.selected);
}
return this.items.find(item => item === this.selected);
},
},
methods: {
getKeyProp (item) {
return this.keyProp ? item[this.keyProp] : item.key || item.identifier;
@@ -25,8 +25,8 @@
type="checkbox"
:checked="isChecked"
:value="value"
@change="handleChange"
:disabled="disabled"
@change="handleChange"
>
<label
class="toggle-switch-label"
@@ -1340,7 +1340,7 @@ export default {
},
openAdminPanel () {
this.$router.push(`/admin-panel/${this.hero._id}`);
this.$router.push(`/admin/panel/${this.hero._id}`);
},
},
};
File diff suppressed because it is too large Load Diff
+9 -1
View File
@@ -39,7 +39,15 @@ export default {
};
const purchaseForKey = currencyToPurchaseForKey[currency];
return window.confirm(this.$t(purchaseForKey, { cost })); // eslint-disable-line no-alert
return new Promise(resolve => {
this.$root.$emit('habitica:purchase-confirm', {
message: this.$t(purchaseForKey, { cost }),
currency,
cost,
resolve,
});
});
},
},
};
@@ -679,7 +679,7 @@ import NotificationMixins from '@/mixins/notifications';
// extract to a shared path
const CONVERSATIONS_PER_PAGE = 10;
const PM_PER_PAGE = 10;
const PM_PER_PAGE = 50;
const UI_STATES = Object.freeze({
LOADING: 'LOADING',
@@ -217,8 +217,18 @@ export default {
}
},
async changeClassAndClose () {
if (!this.classDisabled && !window.confirm(this.$t('changeClassConfirmCost'))) {
return;
if (!this.classDisabled) {
const confirmed = await new Promise(resolve => {
this.$root.$emit('habitica:purchase-confirm', {
message: this.$t('changeClassConfirmCost'),
currency: 'gems',
cost: 3,
resolve,
});
});
if (!confirmed) {
return;
}
}
this.$root.$once('bv::hide::modal', () => {
@@ -1,7 +1,8 @@
<template>
<tr>
<td colspan="3"
<td
v-if="!mixinData.inlineSettingMixin.modalVisible"
colspan="3"
>
<div class="d-flex justify-content-between align-items-center">
<h3
@@ -18,8 +19,9 @@
</a>
</div>
</td>
<td colspan="3"
<td
v-if="mixinData.inlineSettingMixin.modalVisible"
colspan="3"
>
<h3
v-once
@@ -59,8 +61,8 @@
{{ $t('performanceAnalytics') }}
</label>
<toggle-switch
class="mb-auto"
v-model="user.preferences.analyticsConsent"
class="mb-auto"
@change="prefToggled()"
/>
</div>
@@ -151,14 +153,14 @@ import { mapState } from '@/libs/store';
import alert from '@/assets/svg/for-css/alert.svg?raw';
export default {
mixins: [
GenericUserPreferencesMixin,
InlineSettingMixin,
],
components: {
SaveCancelButtons,
ToggleSwitch,
},
mixins: [
GenericUserPreferencesMixin,
InlineSettingMixin,
],
data () {
return {
icons: Object.freeze({
+39 -24
View File
@@ -14,6 +14,8 @@
<bug-report-success-modal v-if="isUserLoaded" />
<external-link-modal />
<birthday-modal />
<purchase-confirm-modal v-if="isUserLoaded" />
<delete-task-confirm-modal v-if="isUserLoaded" />
<template v-if="isUserLoaded">
<privacy-banner />
<chat-banner />
@@ -138,6 +140,8 @@ import paymentsSuccessModal from '@/components/payments/successModal';
import subCancelModalConfirm from '@/components/payments/cancelModalConfirm';
import subCanceledModal from '@/components/payments/canceledModal';
import externalLinkModal from '@/components/externalLinkModal.vue';
import purchaseConfirmModal from '@/components/shops/purchaseConfirmModal.vue';
import deleteTaskConfirmModal from '@/components/tasks/deleteTaskConfirmModal.vue';
import spellsMixin from '@/mixins/spells';
import {
@@ -172,6 +176,8 @@ export default {
bugReportModal,
bugReportSuccessModal,
externalLinkModal,
purchaseConfirmModal,
deleteTaskConfirmModal,
},
mixins: [notifications, spellsMixin],
data () {
@@ -262,7 +268,6 @@ export default {
this.$store.dispatch('user:fetch'),
this.$store.dispatch('tasks:fetchUserTasks'),
]).then(() => {
this.$store.state.isUserLoaded = true;
let analyticsConsent = localStorage.getItem('analyticsConsent');
if (analyticsConsent !== null) {
analyticsConsent = analyticsConsent === 'true';
@@ -270,31 +275,11 @@ export default {
this.$store.dispatch('user:set', { 'preferences.analyticsConsent': analyticsConsent });
}
}
if (window && window['habitica-i18n']) {
if (this.user.preferences.language === window['habitica-i18n'].language.code) {
return null;
}
}
if (window && window['habitica-i18n']) {
if (this.user.preferences.language === window['habitica-i18n'].language.code) {
return null;
}
}
Analytics.updateUser();
return axios.get(
'/api/v4/i18n/browser-script',
{
language: this.user.preferences.language,
headers: {
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
Expires: '0',
},
},
);
return this.loadAllTranslations();
}).then(() => {
const i18nData = window && window['habitica-i18n'];
this.$loadLocale(i18nData);
this.$store.state.isUserLoaded = true;
this.hideLoadingScreen();
// Adjust the timezone offset
@@ -374,6 +359,36 @@ export default {
hideLoadingScreen () {
this.loading = false;
},
async loadContentTranslations () {
const contentTranslations = await axios.get(
'/api/v4/i18n/content',
{
language: this.user.preferences.language,
},
);
const i18nData = window && window['habitica-i18n'];
i18nData.strings = { ...i18nData.strings, ...contentTranslations.data };
this.$loadLocale(i18nData);
},
async loadAllTranslations () {
if (window && window['habitica-i18n']) {
if (this.user.preferences.language === window['habitica-i18n'].language.code) {
return this.loadContentTranslations();
}
}
await axios.get(
'/api/v4/i18n/core',
{
language: this.user.preferences.language,
headers: {
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
Expires: '0',
},
},
);
return this.loadContentTranslations();
},
},
};
</script>
-6
View File
@@ -11,7 +11,6 @@ import { DEPRECATED_ROUTES } from '@/router/deprecated-routes';
// NOTE: when adding a page make sure to implement the `common:setTitle` action
const RegisterLoginReset = () => import(/* webpackChunkName: "auth" */'@/components/auth/registerLoginReset');
const Logout = () => import(/* webpackChunkName: "auth" */'@/components/auth/logout');
// Hall
@@ -79,11 +78,6 @@ const router = new VueRouter({
// in the route component to set a specific subtitle for the page.
routes: [
{ name: 'logout', path: '/logout', component: Logout },
{
name: 'resetPassword', path: '/reset-password', component: RegisterLoginReset, meta: { requiresLogin: false },
}, {
name: 'forgotPassword', path: '/forgot-password', component: RegisterLoginReset, meta: { requiresLogin: false },
},
{ name: 'tasks', path: '/', component: UserTasks },
{
name: 'userProfile',
@@ -57,6 +57,9 @@ export const STATIC_ROUTES = {
{
name: 'features', path: 'features', component: FeaturesPage, meta: { requiresLogin: false },
},
{
name: 'forgotPassword', path: '/forgot-password', component: RegisterLoginReset, meta: { requiresLogin: false },
},
{
name: 'front', path: 'front', component: HomePage, meta: { requiresLogin: false },
},
@@ -90,6 +93,9 @@ export const STATIC_ROUTES = {
{
name: 'register', path: '/register', component: RegisterLoginReset, meta: { requiresLogin: false },
},
{
name: 'resetPassword', path: '/reset-password', component: RegisterLoginReset, meta: { requiresLogin: false },
},
{
name: 'terms', path: 'terms', component: TermsPage, meta: { requiresLogin: false },
},
+1 -1
View File
@@ -3,7 +3,7 @@ import Vue from 'vue';
import * as Analytics from '@/libs/analytics';
export async function getChat (store, payload) {
const response = await axios.get(`/api/v4/groups/${payload.groupId}/chat`);
const response = await axios.get(`/api/v4/groups/${payload.groupId}/chat?limit=400`);
return response.data.data;
}
+1 -2
View File
@@ -2,11 +2,10 @@
"challenge": "Предизвикателство",
"challengeDetails": "Предизвикателствата са обществени събития, в които играчите се състезават и печелят награди като изпълняват няколко свързани по някакъв начин задачи.",
"brokenChaLink": "Повредена връзка на предизвикателство",
"brokenTask": "Повредена връзка на предизвикателство: тази задача е била част от предизвикателство, но е била премахната от него. Какво бихте искали да направите?",
"keepIt": "Запазване",
"removeIt": "Премахване",
"brokenChallenge": "Повредена връзка на предизвикателство: тази задача е била част от предизвикателство, но то (или групата) е било изтрито. Какво бихте искали да направите с останалите задачи?",
"challengeCompleted": "Това предизвикателство е приключило и победителят е <span class=\"badge\"><%- user %></span>! Какво искате да направите с останалите задачи?",
"challengeCompleted": "Това предизвикателство е приключило и победителят е <span class=\"badge\"><%= user %></span>! Какво искате да направите с останалите задачи?",
"unsubChallenge": "Повредена връзка на предизвикателство: тази задача е била част от предизвикателство, но Вие сте се отписали от него. Какво искате да направите с останалите задачи?",
"challenges": "Предизвикателства",
"endDate": "Крайна дата",
+2 -2
View File
@@ -182,7 +182,7 @@
"questEggVelociraptorText": "Велоцираптор",
"questEggVelociraptorMountText": "Велоцираптор",
"questEggVelociraptorAdjective": "умен",
"eggNotes": "Намерете излюпваща отвара, която да излеете върху това яйце и от него ще се излюпи <%= eggAdjective(locale) %> <%= eggText(locale) %>.",
"eggNotes": "Намерете излюпваща отвара, която да излеете върху това яйце и от него ще се излюпи <%= eggAdjective %> <%= eggText %>.",
"hatchingPotionBase": "Нормален цвят",
"hatchingPotionWhite": "Бял цвят",
"hatchingPotionDesert": "Пустинен цвят",
@@ -211,7 +211,7 @@
"hatchingPotionGlow": "Светещо в тъмното",
"hatchingPotionFrost": "Скреж",
"hatchingPotionIcySnow": "Леден сняг",
"hatchingPotionNotes": "Излейте това върху яйце и от него ще се излюпи любимец с(ъс) <%= potText(locale) %>.",
"hatchingPotionNotes": "Излейте това върху яйце и от него ще се излюпи любимец с(ъс) <%= potText %>.",
"foodMeat": "Месо",
"foodMeatThe": "Месото",
"foodMeatA": "Месо",
+1 -1
View File
@@ -190,7 +190,7 @@
"messages": "Съобщения",
"emptyMessagesLine1": "Нямате съобщения",
"emptyMessagesLine2": "Можете да изпратите ново съобщение на потребител, като посетите профила им и докоснете бутона \"Съобщение\".",
"userSentMessage": "<span class=\"notification-bold\"><%- user %></span> Ви изпрати съобщение",
"userSentMessage": "<span class=\"notification-bold\"><%= user %></span> Ви изпрати съобщение",
"letsgo": "Хойде!",
"selected": "Избрано",
"howManyToBuy": "Колко искате да купите?",
+14 -14
View File
@@ -24,14 +24,14 @@
"userId": "Потребителски идентификатор",
"invite": "Покана",
"leave": "Напускане",
"invitedToParty": "Получихте покана за присъединяване към групата <span class=\"notification-bold\"><%- party %></span>",
"invitedToPrivateGuild": "Получихте покана за присъединяване към частната гилдия <span class=\"notification-bold\"><%- guild %></span>",
"invitedToPublicGuild": "Получихте покана за присъединяване към гилдията <span class=\"notification-bold-blue\"><%- guild %></span>",
"invitedToParty": "Получихте покана за присъединяване към групата <span class=\"notification-bold\"><%= party %></span>",
"invitedToPrivateGuild": "Получихте покана за присъединяване към частната гилдия <span class=\"notification-bold\"><%= guild %></span>",
"invitedToPublicGuild": "Получихте покана за присъединяване към гилдията <span class=\"notification-bold-blue\"><%= guild %></span>",
"invitationAcceptedHeader": "Поканата Ви беше приета",
"invitationAcceptedBody": "<%= username %> прие поканата Ви да се присъедини към <%= groupName %>!",
"systemMessage": "Системно съобщение",
"newMsgGuild": "Има нови публикации в <span class=\"notification-bold-blue\"><%- name %></span>",
"newMsgParty": "Има нови публикации в групата Ви — <span class=\"notification-bold-blue\"><%- name %></span>",
"newMsgGuild": "Има нови публикации в <span class=\"notification-bold-blue\"><%= name %></span>",
"newMsgParty": "Има нови публикации в групата Ви — <span class=\"notification-bold-blue\"><%= name %></span>",
"chat": "Съобщения",
"sendChat": "Изпращане на съобщението",
"group": "Група",
@@ -151,14 +151,14 @@
"onlyGroupLeaderCanEditTasks": "Нямате право да управлявате задачите!",
"onlyGroupTasksCanBeAssigned": "Само групови задачи могат да бъдат зададени",
"assignedTo": "Назначена на",
"assignedToUser": "Назначена на <strong><%- userName %></strong>",
"assignedToUser": "Назначена на <strong><%= userName %></strong>",
"assignedToMembers": "Назначена на <strong><%= userCount %> членове </strong>",
"assignedToYouAndMembers": "Назначена на Вас и още <strong><%= userCount %> членове</strong>",
"youAreAssigned": "Тази задача е назначена на Вас",
"taskIsUnassigned": "Тази задача не е зададена на никого",
"confirmUnClaim": "Наистина ли искате да оставите тази задача?",
"confirmNeedsWork": "Наистина ли искате да отбележите, че тази задача се нуждае от още работа?",
"userRequestsApproval": "<strong><%- userName %></strong> иска одобрение",
"userRequestsApproval": "<strong><%= userName %></strong> иска одобрение",
"userCountRequestsApproval": "<strong><%= userCount %> членове</strong> искат одобрение",
"youAreRequestingApproval": "Вие искате одобрение",
"chatPrivilegesRevoked": "Не можете да направите това, защото привилегиите Ви в чата са Ви били отнети. За детайли или запитване за връшане на привилегии, моля пратете email на нашия Обществен Оправител на admin@habitica.com или попитайте вашия родител или настойник да им прати email. Моля, напишете и потребителското си име в писмото. Ако модератор вече ви е казал че блокирането ви към чата е временно, няма нужда да пращате email.",
@@ -168,9 +168,9 @@
"claim": "Вземане на Задача",
"removeClaim": "Отказване от задачата",
"onlyGroupLeaderCanManageSubscription": "Само водачът на групата може да управлява абонамента ѝ",
"yourTaskHasBeenApproved": "Задачата Ви <span class=\"notification-green notification-bold\"><%- taskText %></span>, беше одобрена.",
"taskNeedsWork": "<span class=\"notification-bold\"><%- managerName %></span> отбеляза, че задачата <span class=\"notification-bold\"><%- taskText %></span> се нуждае от още работа.",
"userHasRequestedTaskApproval": "<span class=\"notification-bold\"><%- user %></span> помоли следната задача да бъде одобрена: <span class=\"notification-bold\"><%- taskName %></span>",
"yourTaskHasBeenApproved": "Задачата Ви <span class=\"notification-green notification-bold\"><%= taskText %></span>, беше одобрена.",
"taskNeedsWork": "<span class=\"notification-bold\"><%= managerName %></span> отбеляза, че задачата <span class=\"notification-bold\"><%= taskText %></span> се нуждае от още работа.",
"userHasRequestedTaskApproval": "<span class=\"notification-bold\"><%= user %></span> помоли следната задача да бъде одобрена: <span class=\"notification-bold\"><%= taskName %></span>",
"approve": "Одобряване",
"approveTask": "Одобряване на задачата",
"needsWork": "Нуждае се от още работа",
@@ -183,8 +183,8 @@
"userIsClamingTask": "`<%= username %> пое:` <%= task %>",
"approvalRequested": "Заявено е одобрение",
"cantDeleteAssignedGroupTasks": "Не можете да изтриете груповите задачи, които са Ви разпределени.",
"groupPlanUpgraded": "<strong><%- groupName %></strong> премина към групов план!",
"groupPlanCreated": "Групата <strong><%- groupName %></strong> беше създадена!",
"groupPlanUpgraded": "<strong><%= groupName %></strong> премина към групов план!",
"groupPlanCreated": "Групата <strong><%= groupName %></strong> беше създадена!",
"onlyGroupLeaderCanInviteToGroupPlan": "Само водачът на групата може да кани хора в група с абонамент.",
"paymentDetails": "Подробности за разплащането",
"aboutToJoinCancelledGroupPlan": "На път сте да се присъедините към група, чийто план е прекратен. НЯМА да получите безплатен абонамент.",
@@ -325,8 +325,8 @@
"PMDisabled": "Деактивиране на лични съобщения",
"groupActivityNotificationTitle": "<%= user %> публикува в <%= group %>",
"suggestedGroup": "Предложено, защото сте нови в Habitica.",
"taskClaimed": "<%- userName %> взеха задачата <span class=\"notification-bold\"><%- taskText %></span>.",
"youHaveBeenAssignedTask": "<%- managerName %> ви присвои задачата <span class=\"notification-bold\"><%- taskText %></span>.",
"taskClaimed": "<%= userName %> взеха задачата <span class=\"notification-bold\"><%= taskText %></span>.",
"youHaveBeenAssignedTask": "<%= managerName %> ви присвои задачата <span class=\"notification-bold\"><%= taskText %></span>.",
"userWithUsernameOrUserIdNotFound": "Потребителското име или Потребителският Идентификатор не бяха намерени.",
"usernameOrUserId": "Потребителско име или Потребителски Идентификатор",
"sendGiftToWhom": "На кой бихте искали да пратите подарък?",
+2 -3
View File
@@ -82,13 +82,12 @@
"paymentMethods": "Купуване чрез",
"paymentSuccessful": "Плащането Ви беше успешно!",
"paymentYouReceived": "Получихте:",
"paymentYouSentGems": "Изпратихте на <strong><%- name %></strong>:",
"paymentYouSentSubscription": "Изпратихте на <strong><%- name %></strong> <%= months %>-месечен абонамент за Хабитика.",
"paymentYouSentGems": "Изпратихте на <strong><%= name %></strong>:",
"paymentYouSentSubscription": "Изпратихте на <strong><%= name %></strong> <%= months %>-месечен абонамент за Хабитика.",
"paymentSubBilling": "Абонаментът Ви ще бъде таксуван с <strong>$<%= amount %></strong> всеки <strong><%= months %> месеца</strong>.",
"success": "Готово!",
"classGear": "Снаряжение за класа",
"classGearText": "Поздравления за избора на клас! Добавих новата Ви основна екипировка в инвентара Ви. Погледнете по-долу, за да я екипирате!",
"autoAllocate": "Автоматично разпределяне",
"spells": "Умения",
"skillsTitle": "Умения",
"toDo": "Задача",
+2 -2
View File
@@ -66,8 +66,8 @@
"mountNotOwned": "Не притежавате този прево.",
"feedPet": "Искате ли да дадете <%= text %> на <%= name %>?",
"raisedPet": "Вие отгледахте <%= pet %>!",
"petName": "<%= egg(locale) %> с(ъс) <%= potion(locale) %>",
"mountName": "<%= mount(locale) %> с(ъс) <%= potion(locale) %>",
"petName": "<%= egg %> с(ъс) <%= potion %>",
"mountName": "<%= mount %> с(ъс) <%= potion %>",
"keyToPets": "Ключ от зверилника за любимци",
"keyToPetsDesc": "Освобождаване на всички стандартни любимци, за да можете да ги съберете отново. (Това не засяга любимците от мисии и редките любимци.)",
"keyToMounts": "Ключ от зверилника за превози",
+1 -2
View File
@@ -2,11 +2,10 @@
"challenge": "Výzva",
"challengeDetails": "Výzvy jsou komunitní události, ve kterých hráči soutěží a získávají odměny za plnění úkolů.",
"brokenChaLink": "Nefunkční odkaz na výzvu",
"brokenTask": "Nefunkční odkaz na výzvu: tento úkol byl součástí výzvy, ale byl z ní odstraněn. Co chceš dělat?",
"keepIt": "Ponechat",
"removeIt": "Odstranit",
"brokenChallenge": "Nefunkční odkaz na výzvu: tento úkol byl součástí výzvy, ale ta (nebo skupina, která ji vytvořila) byla odstraněna. Co chceš dělat s osiřelými úkoly?",
"challengeCompleted": "Výzva byla ukončena a vítězem se stal <span class=\"badge\"><%- user %></span>! Co chceš dělat s osiřelými úkoly?",
"challengeCompleted": "Výzva byla ukončena a vítězem se stal <span class=\"badge\"><%= user %></span>! Co chceš dělat s osiřelými úkoly?",
"unsubChallenge": "Nefunkční odkaz na výzvu: tento úkol byl součástí výzvy, ze které jsi se odhlásil/a. Co chceš dělat s osiřelými úkoly?",
"challenges": "Výzvy",
"endDate": "Končí",
+2 -2
View File
@@ -182,7 +182,7 @@
"questEggVelociraptorText": "Velociraptor",
"questEggVelociraptorMountText": "Velociraptor",
"questEggVelociraptorAdjective": "chytrý",
"eggNotes": "Najdi líhnoucí lektvar, nalij ho na vejce a to se vylíhne v <%= eggAdjective(locale) %> <%= eggText(locale) %>.",
"eggNotes": "Najdi líhnoucí lektvar, nalij ho na vejce a to se vylíhne v <%= eggAdjective %> <%= eggText %>.",
"hatchingPotionBase": "Základní",
"hatchingPotionWhite": "Bílý",
"hatchingPotionDesert": "Pouštní",
@@ -211,7 +211,7 @@
"hatchingPotionGlow": "Ve tmě svítící",
"hatchingPotionFrost": "Zmrzlý",
"hatchingPotionIcySnow": "Ledově Sněhový",
"hatchingPotionNotes": "Nalij ho na vejce a vylíhne se ti <%= potText(locale) %> mazlíček.",
"hatchingPotionNotes": "Nalij ho na vejce a vylíhne se ti <%= potText %> mazlíček.",
"foodMeat": "Maso",
"foodMeatThe": "Maso",
"foodMeatA": "Maso",
+1 -1
View File
@@ -190,7 +190,7 @@
"messages": "Zprávy",
"emptyMessagesLine1": "Nemáš žádné zprávy",
"emptyMessagesLine2": "Novou zprávu uživateli/Česku můžeš poslat tak, že navštívíš jeho/její profil a klikneš na tlačítko “Zprávy”.",
"userSentMessage": "<span class=\"notification-bold\"><%- user %></span> ti poslal/a zprávu",
"userSentMessage": "<span class=\"notification-bold\"><%= user %></span> ti poslal/a zprávu",
"letsgo": "Pojďmě!",
"selected": "Vybrané",
"howManyToBuy": "Kolik by jsi chtěl koupit?",
+15 -15
View File
@@ -24,14 +24,14 @@
"userId": "Uživatelské ID",
"invite": "Pozvat",
"leave": "Odejít",
"invitedToParty": "Byl jsi pozván do družiny <span class=\"notification-bold\"><%- party %></span>",
"invitedToPrivateGuild": "Byl jsi pozván do soukromého cechu <span class=\"notification-bold\"><%- guild %></span>",
"invitedToPublicGuild": "Byl jsi pozván do cechu <span class=\"notification-bold-blue\"><%- guild %></span>",
"invitedToParty": "Byl jsi pozván do družiny <span class=\"notification-bold\"><%= party %></span>",
"invitedToPrivateGuild": "Byl jsi pozván do soukromého cechu <span class=\"notification-bold\"><%= guild %></span>",
"invitedToPublicGuild": "Byl jsi pozván do cechu <span class=\"notification-bold-blue\"><%= guild %></span>",
"invitationAcceptedHeader": "Tvá pozvánka byla přijata",
"invitationAcceptedBody": "<%= username %> přijal tvoji pozvánku do <%= groupName %>!",
"systemMessage": "Systémová zpráva",
"newMsgGuild": "<span class=\"notification-bold-blue\"><%- name %></span> má nový příspěvek",
"newMsgParty": "Tvá družina, <span class=\"notification-bold-blue\"><%- name %></span>, má nový příspěvek",
"newMsgGuild": "<span class=\"notification-bold-blue\"><%= name %></span> má nový příspěvek",
"newMsgParty": "Tvá družina, <span class=\"notification-bold-blue\"><%= name %></span>, má nový příspěvek",
"chat": "Chat",
"sendChat": "Poslat zprávu",
"group": "Skupina",
@@ -151,14 +151,14 @@
"onlyGroupLeaderCanEditTasks": "Not authorized to manage tasks!",
"onlyGroupTasksCanBeAssigned": "Only group tasks can be assigned",
"assignedTo": "Přiřadit k",
"assignedToUser": "Přiřazeno <strong><%- userName %></strong>",
"assignedToUser": "Přiřazeno <strong><%= userName %></strong>",
"assignedToMembers": "Přiřazeno <strong><%= userCount %> members</strong>",
"assignedToYouAndMembers": "Přiřazeno vám a <strong><%= userCount %> members</strong>",
"youAreAssigned": "Jsi přiřazen/a k tomuto úkolu",
"taskIsUnassigned": "This task is unassigned",
"confirmUnClaim": "Are you sure you want to unclaim this task?",
"confirmNeedsWork": "Are you sure you want to mark this task as needing work?",
"userRequestsApproval": "<strong><%- userName %></strong> požaduje schválení",
"userRequestsApproval": "<strong><%= userName %></strong> požaduje schválení",
"userCountRequestsApproval": "<strong><%= userCount %> members</strong> požadují schválení",
"youAreRequestingApproval": "You are requesting approval",
"chatPrivilegesRevoked": "Toto nelze provést, protože vaše oprávnění k chatu byla odstraněna. Chcete-li získat další informace nebo se zeptat, zda lze vaše oprávnění vrátit, pošlete e-mail našemu komunitnímu manažerovi na adrese admin@habitica.com nebo požádejte svého rodiče nebo zákonného zástupce o zaslání e-mailu. Do e-mailu uveďte prosím své @uživatelskéjméno. Pokud vám moderátor již řekl, že váš zákaz chatu je dočasný, nemusíte posílat e-maily.",
@@ -168,9 +168,9 @@
"claim": "Nárokovat úkol",
"removeClaim": "Remove Claim",
"onlyGroupLeaderCanManageSubscription": "Only the group leader can manage the group's subscription",
"yourTaskHasBeenApproved": "Váš úkol <span class=\"notification-green notification-bold\"><%- taskText %></span> byl schválený.",
"taskNeedsWork": "<span class=\"notification-bold\"><%- managerName %></span> marked <span class=\"notification-bold\"><%- taskText %></span> as needing additional work.",
"userHasRequestedTaskApproval": "<span class=\"notification-bold\"><%- user %></span> requests approval for <span class=\"notification-bold\"><%- taskName %></span>",
"yourTaskHasBeenApproved": "Váš úkol <span class=\"notification-green notification-bold\"><%= taskText %></span> byl schválený.",
"taskNeedsWork": "<span class=\"notification-bold\"><%= managerName %></span> marked <span class=\"notification-bold\"><%= taskText %></span> as needing additional work.",
"userHasRequestedTaskApproval": "<span class=\"notification-bold\"><%= user %></span> requests approval for <span class=\"notification-bold\"><%= taskName %></span>",
"approve": "Approve",
"approveTask": "Approve Task",
"needsWork": "Needs Work",
@@ -183,8 +183,8 @@
"userIsClamingTask": "`<%= username %> has claimed:` <%= task %>",
"approvalRequested": "Approval Requested",
"cantDeleteAssignedGroupTasks": "Can't delete group tasks that are assigned to you.",
"groupPlanUpgraded": "<strong><%- groupName %></strong> was upgraded to a Group Plan!",
"groupPlanCreated": "<strong><%- groupName %></strong> was created!",
"groupPlanUpgraded": "<strong><%= groupName %></strong> was upgraded to a Group Plan!",
"groupPlanCreated": "<strong><%= groupName %></strong> was created!",
"onlyGroupLeaderCanInviteToGroupPlan": "Only the group leader can invite users to a group with a subscription.",
"paymentDetails": "Payment Details",
"aboutToJoinCancelledGroupPlan": "You are about to join a group with a canceled plan. You will NOT receive a free subscription.",
@@ -321,8 +321,8 @@
"allAssignedCompletion": "All - Completes when all assigned users finish",
"groupActivityNotificationTitle": "<%= user %> publikoval v <%= group %>",
"suggestedGroup": "Navrženo, protože jste v Habitica nový/á.",
"taskClaimed": "<%- userName %> nárokoval úkol <span class=\"notification-bold\"><%- taskText %></span>.",
"youHaveBeenAssignedTask": "<%- managerName %> vám přidělil úkol <span class=\"notification-bold\"><%- taskText %></span>.",
"taskClaimed": "<%= userName %> nárokoval úkol <span class=\"notification-bold\"><%= taskText %></span>.",
"youHaveBeenAssignedTask": "<%= managerName %> vám přidělil úkol <span class=\"notification-bold\"><%= taskText %></span>.",
"pmReported": "Děkujeme za nahlášení této zprávy.",
"newPartyPlaceholder": "Zadej jméno tvé družiny.",
"userWithUsernameOrUserIdNotFound": "Uživatelské jméno nebo uživatelské ID nebylo nalezeno.",
@@ -336,7 +336,7 @@
"PMDisabled": "Zakaž soukromé zprávy",
"unassigned": "Nepřiřazeno",
"claimRewards": "Vyzvedni si odměnu",
"assignedDateAndUser": "Přiřazeno uživatelem/kou <strong>@<%- username %></strong> dne <strong><%= date %></strong>",
"assignedDateAndUser": "Přiřazeno uživatelem/kou <strong>@<%= username %></strong> dne <strong><%= date %></strong>",
"assignedDateOnly": "Přiřazeno k <strong><%= date %></strong>",
"managerNotes": "Poznámky manažera",
"thisTaskApproved": "Tento úkol byl schválen",
+2 -3
View File
@@ -82,13 +82,12 @@
"paymentMethods": "Platební metody",
"paymentSuccessful": "Tvá platba proběhla úspěšně!",
"paymentYouReceived": "Obdržel jsi:",
"paymentYouSentGems": "Poslal/a jsi <strong><%- name %></strong>:",
"paymentYouSentSubscription": "Poslal/a jsi <strong><%- name %></strong> předplatné na <%= months %>-měsíce/ů v Habitica.",
"paymentYouSentGems": "Poslal/a jsi <strong><%= name %></strong>:",
"paymentYouSentSubscription": "Poslal/a jsi <strong><%= name %></strong> předplatné na <%= months %>-měsíce/ů v Habitica.",
"paymentSubBilling": "Tvoje předplatné ve výši <strong>$<%= amount %></strong> bude účtovano každé/ých <strong><%= months %> měsíce/ů </strong>.",
"success": "Úspěch!",
"classGear": "Vybavení pro tvé povolání",
"classGearText": "Gratuluji k vybrání povolání! Přidal jsem ti základní zbraň do tvého inventáře. Podívej se dolů a vybav se!",
"autoAllocate": "Připisovat automaticky",
"spells": "Dovednosti",
"skillsTitle": "<%= classStr %> Dovednosti",
"toDo": "úkol",
+2 -2
View File
@@ -66,8 +66,8 @@
"mountNotOwned": "Nevlastníš toto jezdecké zvíře.",
"feedPet": "Dát <%= text %> svému <%= name %>?",
"raisedPet": "Vychoval jsi svého <%= pet %>!",
"petName": "<%= potion(locale) %> <%= egg(locale) %>",
"mountName": "<%= potion(locale) %> <%= mount(locale) %>",
"petName": "<%= potion %> <%= egg %>",
"mountName": "<%= potion %> <%= mount %>",
"keyToPets": "Klíč ke Kotcům Mazlíčků",
"keyToPetsDesc": "Propusť všechny své běžné mazlíčky abys je mohl sbírat znovu. (Ti vzácní a z výprav tím nebudou ovlivněni.)",
"keyToMounts": "Klíč ke Kotcům Zvířat",
-1
View File
@@ -121,7 +121,6 @@
"yesterDailiesCallToAction": "Začít můj nový den!",
"sessionOutdated": "Tvá relace je zastaralá. Prosím, zkus ji obnovit nebo synchronizovat.",
"errorTemporaryItem": "Tento předmět je dočasný a nemůže být připnut.",
"sureDeleteType": "Chceš tento <%= type %> opravdu smazat?",
"deleteTaskType": "Tento <%= type %> smazat",
"addNotes": "Přidej poznámky",
"addATitle": "Přidej název",
+1 -2
View File
@@ -2,11 +2,10 @@
"challenge": "Udfordring",
"challengeDetails": "Udfordringer er fællesskabsevents, i hvilke spillere konkurrerer og vinder belønninger ved at fuldføre en gruppe relaterede opgaver.",
"brokenChaLink": "Defekt udfordringslink",
"brokenTask": "Defekt udfordringslink: denne opgave var en del af en udfordring, men er blevet fjernet fra den. Hvad vil du gøre?",
"keepIt": "Behold den",
"removeIt": "Fjern den",
"brokenChallenge": "Defekt udfordringslink: denne opgave var en del af en udfordring, men udfordringen (eller gruppen) er blevet fjernet. Hvad vil du gøre med de gruppeløse opgaver?",
"challengeCompleted": "Denne udfordring er afsluttet, og vinderen blev <span class=\"badge\"><%- user %></span>! Hvad vil du gøre med de gruppeløse opgaver?",
"challengeCompleted": "Denne udfordring er afsluttet, og vinderen blev <span class=\"badge\"><%= user %></span>! Hvad vil du gøre med de gruppeløse opgaver?",
"unsubChallenge": "Defekt Udfordringslink: denne opgave var en del af en udfordring, som du ikke længere abonnerer på. Hvad vil du gøre med de gruppeløse opgaver?",
"challenges": "Udfordringer",
"endDate": "Afsluttes",
+7 -2
View File
@@ -83,7 +83,7 @@
"allocatePerPop": "Tilføj et point til Opfattelse",
"allocateInt": "Point tilføjet til Intelligens:",
"allocateIntPop": "Tilføj et point til Intelligens",
"noMoreAllocate": "Nu hvor du har nået niveau 100, vil du ikke optjene flere Egenskabspoint. Du kan fortsætte, eller starte et nyt eventyr fra niveau 1 ved at bruge <a href='http://habitica.fandom.com/wiki/Orb_of_Rebirth' target='_blank'>Genfødselskuglen</a>!",
"noMoreAllocate": "Nu hvor du har nået niveau 100, vil du ikke optjene flere Egenskabspoint. Du kan fortsætte, eller starte et nyt eventyr fra niveau 1 ved at bruge<a href='/shops/market'>Genfødselskugle</a>.",
"stats": "Egenskaber",
"strength": "Styrke",
"strText": "Styrke forøger chancen for tilfældige \"fuldtræffere\" og giver et boost til Guld, Erfaring, og chancen for at finde genstande fra dem. Det hjælper også med at påføre bossmonstre skade.",
@@ -183,5 +183,10 @@
"chatCastSpellParty": "<%= username %> kaster <%= spell %> for holdet.",
"notEnoughGold": "Ikke nok guld.",
"purchasePetItemConfirm": "Dette køb vil overskride det antal genstande du skal bruge for at klække alle mulige <%= itemText %> Kæledyr. Er du sikker?",
"purchaseForGold": "Køb for <%= cost %> Guld?"
"purchaseForGold": "Køb for <%= cost %> Guld?",
"skins": "Hudfarve",
"titleFacialHair": "Ansigts hår",
"titleHaircolor": "Hårfarve",
"titleHairbase": "Hår stil",
"strTaskText": "Øg chancen for fuldtræffer slag og skade ved at færdiggøre opgaver. Dette øger også skade mod missions monstrene."
}
+2 -2
View File
@@ -182,7 +182,7 @@
"questEggVelociraptorText": "Velociraptor",
"questEggVelociraptorMountText": "Velociraptor",
"questEggVelociraptorAdjective": "en vaks",
"eggNotes": "Find en udrugningseliksir til at hælde på dit æg, og det vil udklække <%= eggAdjective(locale) %> <%= eggText(locale) %>.",
"eggNotes": "Find en udrugningseliksir til at hælde på dit æg, og det vil udklække <%= eggAdjective %> <%= eggText %>.",
"hatchingPotionBase": "Almindelig",
"hatchingPotionWhite": "Hvid",
"hatchingPotionDesert": "Ørken",
@@ -211,7 +211,7 @@
"hatchingPotionGlow": "Selvlysende",
"hatchingPotionFrost": "Frost",
"hatchingPotionIcySnow": "Isnende sne",
"hatchingPotionNotes": "Hæld denne over et æg, og det vil udklækkes til et <%= potText(locale) %> kæledyr.",
"hatchingPotionNotes": "Hæld denne over et æg, og det vil udklækkes til et <%= potText %> kæledyr.",
"foodMeat": "Kød",
"foodMeatThe": "Kødet",
"foodMeatA": "Kød",
+12 -1
View File
@@ -13,5 +13,16 @@
"faqQuestion28": "Kan jeg sætte mine Daglige på pause, hvis jeg har brug for en pause?",
"webFaqAnswer28": "Ja! Knappen \"Pause Damage\" kan findes i Indstillinger. Det vil forhindre dig i at miste HP for mistede Daglige. Dette er nyttigt, hvis du er på ferie, har brug for et hvil, eller du af en eller anden grund måske har brug for en pause. Hvis du deltager i en Quest, vil dine egne afventende fremskridt blive sat på pause, men du vil stadig tage skade fra dit partimedlems missede Daglige.\n\nFor at sætte specifikke Daglige på pause, kan du redigere tidsplanen, så den forfalder hver 0. dag, indtil du er klar til at genstarte den.",
"faqQuestion29": "Hvordan gendanner jeg HP?",
"webFaqAnswer29": "Du kan genvinde 15 HP ved at købe en Health Potion fra din belønningskolonne for 25 guld. Derudover vil du altid genvinde fuld HP, når du stiger i niveau!"
"webFaqAnswer29": "Du kan genvinde 15 HP ved at købe en Health Potion fra din belønningskolonne for 25 guld. Derudover vil du altid genvinde fuld HP, når du stiger i niveau!",
"webFaqAnswer34": "Dyr kan lide mad som matcher deres farve, med undtagelse af basis dyr som alle kan lide det samme mad, uanset farve. Du kan se hvilken specifik mad de forskellige dyr kan lide, herunder: \n\n * Basis dyr kan lide kød\n * Hvide dyr kan lide mælk\n * Ørken dyr kan lide kartofler\n * Røde dyr kan lide jordbær\n * Skygge dyr kan lide chokolade\n * Skelet dyr kan lide fisk\n * Zombie dyr kan lide kød\n * Lyserøde candyfloss dyr kan lide lyserød candyfloss\n * Blå candyfloss dyr kan lide blå candyfloss\n * Gyldne dyr kan lide honning",
"faqQuestion30": "Hvad sker der når jeg løber tør for SP?",
"webFaqAnswer30": "Hvis din SP når ned på nul, mister du et level - det levels egenskabspoint, alt dit guld, og et stykke udstyr som kan købes igen. Du kan genvinde SP ved at færdiggøre opgaver, og ved at gå op i level igen.",
"faqQuestion31": "Hvorfor mistede jeg SP ved at gennemføre en ikke-negativ opgave?",
"webFaqAnswer31": "Hvis du gennemfører en opgave og mister SP du ikke burde have mistet, er der opstået en forsinkelse imens serveren synkroniserede ændringer mellem dine enheder. Fx. Hvis du bruger Guld, Magisk energi, eller mister SP på din telefon og efterfølgende gennemfører en opgave på hjemmesiden, mangler serveren bare at synkronisere dine enheder.",
"faqQuestion32": "Hvordan vælger jeg klassering?",
"webFaqAnswer32": "Alle spillere starter i kriger-klassen indtil de når level 10. Når du når level 10 får du valget mellem at skifte klassering eller fortsætte som kriger.\n\nHver klasse har forskelligt udstyr og færdigheder. Hvis du ikke har lyst til at skifte klassering, kan du trykke på “fravælg”. Hvis du vælger ikke at vælge klassering, kan du altid aktivere klasseringssystemet i indstillingerne senere.\n\nHvis du gerne vil skifte klasse efter level 10, kan du gøre dette ved at bruge en genfødselskugle. Genfødselskuglen vil være mulig at købe på markedet for 6 ædelsten når du når level 50, eller få den gratis ved at nå level 100.\n\nAlternativt, kan du på ethvert tidspunkt ændre klassering i indstillinger for 3 ædelsen. Dette nulstiller ikke dit level, som ellers sker ved brug af genfødselskuglen. Det vil derimod give dig mulighed for at omrokere de egenskabspoint du har samlet sammen ved at gå op i level, til at matche din nye klasse.",
"faqQuestion33": "Hvad er den blå bjælke som kommer efter level 10?",
"webFaqAnswer33": "Efter du låser op for klasseringssystemet kan du også låse op for færdigheder, som kræver at Magisk energi benyttes. Magisk energi bestemmes ud fra din intelligensstatus, og kan justeres med færdigheder og visse udstyr.",
"faqQuestion34": "Hvilken slags mad kan mit dyr lide?",
"faqQuestion35": "Mit dyr forsvandt da jeg fodrede det! Hvad skete der?"
}
+39 -27
View File
@@ -24,34 +24,34 @@
"invalidEmail": "Det kræver en valid emailadresse for at få nulstillet dit kodeord.",
"login": "Log ind",
"logout": "Log Ud",
"marketing1Header": "Forbedr dine vaner ved at spille et spil",
"marketing1Lead1Title": "Dit liv, rollespillet",
"marketing1Lead1": "Habitica er et computerspil, der hjælper med at forbedre dine vaner i virkeligheden. Det gør dit liv til et spil ved at lave alle dine opgaver (Vaner, Daglige og To-Do's) indtil små monstre, du skal besejre. Jo bedre du er til dette, desto større fremskridt vil du gøre i spillet. Hvis du begår fejl i livet, vil din karakter miste fremskridt i spillet.",
"marketing1Lead2Title": "Få Lækkert Udstyr",
"marketing1Lead2": "Forbedr dine vaner for at ændre din avatar. Vis det fede udstyr du har tjent!",
"marketing1Lead3Title": "Find Tilfældige Præmier",
"marketing1Header": "Forbedr dine vaner med spil, et level ad gangen!",
"marketing1Lead1Title": "Gør dit liv til et spil",
"marketing1Lead1": "Habitica er den perfekte app for alle der har svært ved at færdiggøre sin to-do liste. Vi bruger genkendelige spillemekanismer, såsom at belønne dig med Guld, Erfarring og genstande, for at hjælpe dig med at føle mere produktiv og samtidig øge følelsen af præstation, når du færdiggøre en opgave. Jo bedre du er til at færdiggøre dine opgaver, jo længere kommer du i spillet.",
"marketing1Lead2Title": "Udstyr dig med stil",
"marketing1Lead2": "Saml svær, udrustning og meget andet med Guld du tjener ved at gennemføre opgaver. Med mulighed for at indsamle, og vælge, mellem hundredevis af forskellige genstande, løber du aldrig tør for sammensætninger. Optimer for niveau, stil, eller begge! ",
"marketing1Lead3Title": "Bliv belønnet for din indsats",
"marketing1Lead3": "For nogle er det chancen, der motiverer dem: et system der hedder \"stokastiske belønninger.\" Habitica har plads til alle slags måder at forbedre eller straffe sig selv på: positive, negative, forudsigelige og tilfældige.",
"marketing2Header": "Kæmp med venner, deltag i interessegrupper",
"marketing2Header": "Slå dig sammen med dine venner",
"marketing2Lead1Title": "Social produktivitet",
"marketing2Lead1": "Selvom du selvfølgelig kan spille Habitica selv, bliver det først virkelig godt når I begynder at samarbejde, konkurrere og holde hinanden ansvarlige. Den mest effektive del af ethvert selvforbedringsprogram er social ansvarlighed, og hvad er et bedre miljø for ansvarlighed og konkurrence end et computerspil?",
"marketing2Lead2Title": "Bekæmp monstre",
"marketing2Lead2Title": "Bekæmp monstre i opgaver",
"marketing2Lead2": "Hvad er et rollespil uden slåskampe? Bekæmp monstre med dit hold. Monstre er \"super-ansvarligheds-mode\" - en dag, du ikke træner, er en dag, hvor monstret skader *alle!*",
"marketing2Lead3Title": "Udfordr Hinanden",
"marketing2Lead3Title": "Udfordr hinanden",
"marketing2Lead3": "Udfordringer lader dig konkurrere med venner og fremmede. Den, der er bedst i slutningen af en udfordring vinder særlige præmier.",
"marketing3Header": "Apps og Udvidelser",
"marketing3Header": "Forskellige måder at bruge Habitica på",
"marketing3Lead1": "**iPhone & Android** apps lader dig klare dine ting på farten. Vi ved, at det nogen gange er for meget at skulle logge ind på websiden for at klikke på knapper.",
"marketing3Lead2Title": "Integrationer",
"marketing3Lead2Title": "Udviklet af fællesskabet",
"marketing3Lead2": "Andre **tredjepartsværktøjer** kan binde Habitica sammen med andre dele af dit liv. Vores API muliggør integrationer som [Chrome Extension](https://chrome.google.com/webstore/detail/habitica/pidkmpibnnnhneohdgjclfdjpijggmjj?hl=en-US), med hvilken du mister point ved at bruge unyttige hjemmesider, og optjener point når du browser de nyttige i stedet. [Se mere her](https://habitica.fandom.com/wiki/Extensions,_Add-Ons,_and_Customizations).",
"marketing4Header": "Organisatorisk brug",
"marketing4Header": "Udover hverdagens pligter",
"marketing4Lead1": "Uddannelse er en af de bedste områder at bruge spilelementer. Vi ved alle, hvordan studerende nærmest er limet til deres telefon disse dage, så brug dette! Sæt dine elever til at kæmpe mod hinanden som hyggelig konkurrence. Beløn god opførsel med sjældne præmier. Se deres karakterer og opførsel blive forbedret.",
"marketing4Lead1Title": "Brug af Spilelementer i Undervisning",
"marketing4Lead1Title": "Spilelementer i undervisning",
"marketing4Lead2": "Prisen for sundhedssektoren stiger, og der må ske noget. Hundredevis af programmer er bygget til at reducere udgifter og forbedre velvære. Vi tror på, at Habitica kan bane vejen for en sundere livsstil.",
"marketing4Lead2Title": "Brug af Spilelementer i Sundhed og Velvære",
"marketing4Lead3-1": "Vil du gøre dit liv til et spil?",
"marketing4Lead2Title": "Spilelementer i sundhed og velvære",
"marketing4Lead3-1": "Klar til at udrette noget imens du har det sjovt?",
"marketing4Lead3-2": "Interesseret i at lede en gruppe for uddannelse, velvære og mere?",
"marketing4Lead3Title": "Spilificér Alt",
"mobileAndroid": "Android",
"mobileIOS": "iOS",
"marketing4Lead3Title": "Start din rejse!",
"mobileAndroid": "Android App",
"mobileIOS": "iOS App",
"oldNews": "Nyheder",
"setNewPass": "Sæt nyt kodeord",
"password": "Kodeord",
@@ -110,7 +110,7 @@
"missingPassword": "Manglende kodeord.",
"missingNewPassword": "Manglende nyt kodeord.",
"invalidEmailDomain": "Du kan ikke registrere med emails med følgende domæner: <%= domains %>",
"wrongPassword": "Forkert kodeord.",
"wrongPassword": "Forkert kodeord. Hvis du har glemt dit kodeord, tryk på “Glemt kodeord”.",
"incorrectDeletePhrase": "Skriv venligst <%= magicWord %> med store bogstaver for at slette din konto.",
"notAnEmail": "Ugyldig e-mailadresse.",
"emailTaken": "E-mailadressen er allerede brugt til en konto.",
@@ -121,8 +121,8 @@
"usernameTaken": "Brugernavn allerede taget.",
"passwordConfirmationMatch": "Kodeord og godkendelse er ikke ens.",
"passwordResetPage": "Nulstil kodeord",
"passwordReset": "Hvis vi har din email på lager, vil instruktioner til opsætning af nyt kodeord være sendt til din email.",
"invalidLoginCredentialsLong": "Åh-åh - din emailaddresse/brugernavn eller kodeord er forkert.\n- Vær sikker på du har indtastet korrekt. Der gøres forskel på store og små bogstaver.\n- Du kan have brugt Facebook eller Google til at oprette din konto, ikke email, så dobbeltcheck ved at prøve dem.\n- Hvis du har glemt dit kodeord, så klik på \"Glemt kodeord\".",
"passwordReset": "Hvis din E-mail eller brugernavn allerede findes i vores system, vil instruktioner til opsætning af nyt kodeord være sendt til din email.",
"invalidLoginCredentialsLong": "Åh-åh - din e-mailadresse, brugernavn eller kodeord er forkert.\nPrøv igen, eller klik på \"Glemt kodeord\".",
"invalidCredentials": "Der er ingen konto med disse legitimationsoplysninger.",
"accountSuspended": "Denne konto, bruger-ID \"<%= userId %>\", er blevet blokeret for at bryde Retningslinjerne for fællesskabet (https://habitica.com/static/community-guidelines) eller vores Vilkår og betingelser (https://habitica.com/static/terms). For at få detaljer eller bede om at få blokaden ophævet, så send venligst en email til vores Community Manager på <%= communityManagerEmail %>, eller bed din forælder eller værge om at gøre det. Inkluder venligst dit @Brugernavn i emailen.",
"accountSuspendedTitle": "Kontoen er blevet suspenderet",
@@ -132,14 +132,14 @@
"invalidReqParams": "Ugyldige anmodningsparametre.",
"memberIdRequired": "\"member\" skal være et gyldigt Unikt Bruger-ID.",
"heroIdRequired": "\"heroID\" skal være et gyldigt Unikt Bruger-ID.",
"cannotFulfillReq": "Din anmodning kan ikke udføres. Kontakt admin@habitica.com hvis fejlen fortsætter.",
"cannotFulfillReq": "Denne e-mail er allerede taget i brug. Prøv at logge ind eller brug en anden e-mail til registrering. Hvis du har brug for hjælp, kontakt os på admin@habitica.com",
"modelNotFound": "Denne model findes ikke.",
"signUpWithSocial": "Tilmeld med <%= social %>",
"signUpWithSocial": "Fortsæt med <%= social %>",
"loginWithSocial": "Log in med<%= social %>",
"confirmPassword": "Bekræft kodeord",
"usernameLimitations": "Brugernavn skal være 1 til 20 tegn, kun bruge bogstaver fra a til z, tal fra 0 til 9, bindestreg eller bundstreg (_), og kan ikke inkludere nogle upassende ord.",
"usernameLimitations": "Brugernavn kan ændres på alle tidspunkter. Det skal være 1 til 20 tegn, kun indeholde bogstaver fra a til z, tal fra 0 til 9, bindestreg eller understreg (_)",
"usernamePlaceholder": "fx HabitRabbit",
"emailPlaceholder": "e.g., rabbit@example.com",
"emailPlaceholder": "Fx., kanin@eksempel.com",
"passwordPlaceholder": "e.g., ******************",
"confirmPasswordPlaceholder": "Sikr dig at det er det samme kodeord!",
"joinHabitica": "Opret bruger",
@@ -169,10 +169,22 @@
"joinMany": "Tilslut dig over <%= userCountInMillions %> millioner andre, der har det sjovt imens de opnår deres mål!",
"joinToday": "Tilmeld dig Habitica i dag",
"signup": "Tilmeld dig",
"getStarted": "Kom i gang!",
"getStarted": "Kom i gang",
"mobileApps": "Mobile apps",
"learnMore": "Lær mere",
"communityInstagram": "Instagram",
"minPasswordLength": "Kodeord skal bestå af 8 eller flere tegn.",
"enterHabitica": "Spil Habitica"
"enterHabitica": "Spil Habitica",
"footerProduct": "Produkt",
"marketing4Lead3Button": "Start i dag",
"incorrectResetPhrase": "Skriv <%= magicWord %> i store bogstaver for at nulstille din konto.",
"minPasswordLengthLogin": "Dit kodeord er minimum 8 karakterer langt.",
"enterValidEmail": "Indtast venligst en gyldig e-mailadresse.",
"translateHabitica": "Oversæt Habitica",
"whatToCallYou": "Hvad skal vi kalde dig?",
"acceptPrivacyTOS": "Bekræfter du at du er over 18 år gammel, og har læst og accepteret vores <a href='/static/terms' target='_blank'>Terms of Services</a> og <a href='/static/privacy' target='_blank'>Privatlivspolitik</a>",
"emailBlockedRegistration": "Denne e-mail er blokeret, og kan ikke benyttes til registrering. Hvis der er sket en fejl, kontakt os på admin@habitica.com",
"emailUsernamePlaceholder": "Fx., vanedyr eller kanin@eksempel.com",
"marketing3Lead1Title": "Android & iOS apps",
"socialAlreadyExists": "Dette login benyttes allerede af en eksisterende Habitica konto."
}
+1 -1
View File
@@ -190,7 +190,7 @@
"messages": "Beskeder",
"emptyMessagesLine1": "Du har ingen beskeder",
"emptyMessagesLine2": "Du kan sende en ny besked til en bruger ved at besøge deres profil og klikke på \"Besked\"-knappen.",
"userSentMessage": "<span class=\"notification-bold\"><%- user %></span> sendte en besked",
"userSentMessage": "<span class=\"notification-bold\"><%= user %></span> sendte en besked",
"letsgo": "Lad os komme i gang!",
"selected": "Valgt",
"howManyToBuy": "Hvor mange vil du købe?",
+13 -13
View File
@@ -24,14 +24,14 @@
"userId": "Bruger-ID",
"invite": "Invitér",
"leave": "Forlad",
"invitedToParty": "Du blev inviteret til at være med på Holdet <span class=\"notification-bold\"><%- party %></span>",
"invitedToPrivateGuild": "Du blev inviteret til den private Klan <span class=\"notification-bold\"><%- guild %></span>",
"invitedToPublicGuild": "Du blev inviteret til Klanen <span class=\"notification-bold-blue\"><%- guild %></span>",
"invitedToParty": "Du blev inviteret til at være med på Holdet <span class=\"notification-bold\"><%= party %></span>",
"invitedToPrivateGuild": "Du blev inviteret til den private Klan <span class=\"notification-bold\"><%= guild %></span>",
"invitedToPublicGuild": "Du blev inviteret til Klanen <span class=\"notification-bold-blue\"><%= guild %></span>",
"invitationAcceptedHeader": "Din invitation blev accepteret",
"invitationAcceptedBody": "<%= username %> har accepteret din invitation til <%= groupName %>!",
"systemMessage": "Systembesked",
"newMsgGuild": "<span class=\"notification-bold-blue\"><%- name %></span> har nye indlæg",
"newMsgParty": "Dit Hold, <span class=\"notification-bold-blue\"><%- name %></span>, har nye indlæg",
"newMsgGuild": "<span class=\"notification-bold-blue\"><%= name %></span> har nye indlæg",
"newMsgParty": "Dit Hold, <span class=\"notification-bold-blue\"><%= name %></span>, har nye indlæg",
"chat": "Chat",
"sendChat": "Send besked",
"group": "Gruppe",
@@ -151,14 +151,14 @@
"onlyGroupLeaderCanEditTasks": "Du har ikke rettigheder til at administrere opgaver!",
"onlyGroupTasksCanBeAssigned": "Kun gruppeopgaver kan blive tildelt",
"assignedTo": "Tildelt",
"assignedToUser": "Tildelt <%- userName %>",
"assignedToUser": "Tildelt <%= userName %>",
"assignedToMembers": "Tildelt <%= userCount %> medlemmer",
"assignedToYouAndMembers": "Tildelt dig og <%= userCount %> medlemmer",
"youAreAssigned": "Du er blevet bedt om at udføre denne opgave",
"taskIsUnassigned": "Denne opgave er ikke tildelt nogen",
"confirmUnClaim": "Er du sikker på, du vil give afkald på denne opgave?",
"confirmNeedsWork": "Er du sikker på, du vil markere denne opgave som ufuldstændig?",
"userRequestsApproval": "<%- userName %> anmoder om godkendelse",
"userRequestsApproval": "<%= userName %> anmoder om godkendelse",
"userCountRequestsApproval": "<%= userCount %> medlemmer anmoder om godkendelse",
"youAreRequestingApproval": "Du har anmodet om godkendelse",
"chatPrivilegesRevoked": "Dine chatprivilegier er blevet inddraget, så du kan ikke udføre denne handling.",
@@ -168,9 +168,9 @@
"claim": "Gør krav på",
"removeClaim": "Giv afkald på",
"onlyGroupLeaderCanManageSubscription": "Kun gruppelederen kan styre gruppens abonnement",
"yourTaskHasBeenApproved": "Din opgave, <span class=\"notification-green\"><%- taskText %></span>, er blevet godkendt.",
"taskNeedsWork": "<span class=\"notification-bold\"><%- managerName %></span> markerede <span class=\"notification-bold\"><%- taskText %></span> som ufuldstændig.",
"userHasRequestedTaskApproval": "<span class=\"notification-bold\"><%- user %></span> anmoder om godkendelse for <span class=\"notification-bold\"><%- taskName %></span>",
"yourTaskHasBeenApproved": "Din opgave, <span class=\"notification-green\"><%= taskText %></span>, er blevet godkendt.",
"taskNeedsWork": "<span class=\"notification-bold\"><%= managerName %></span> markerede <span class=\"notification-bold\"><%= taskText %></span> som ufuldstændig.",
"userHasRequestedTaskApproval": "<span class=\"notification-bold\"><%= user %></span> anmoder om godkendelse for <span class=\"notification-bold\"><%= taskName %></span>",
"approve": "Godkend",
"approveTask": "Godkend opgave",
"needsWork": "Ufuldstændig",
@@ -183,8 +183,8 @@
"userIsClamingTask": "`<%= username %> har gjort krav på:` <%= task %>",
"approvalRequested": "Godkendelse efterspurgt",
"cantDeleteAssignedGroupTasks": "Du kan ikke slette gruppeopgaver, der er blevet tildelt dig.",
"groupPlanUpgraded": "<strong><%- groupName %></strong> blev opgraderet til en Gruppeplan!",
"groupPlanCreated": "<strong><%- groupName %></strong> blev oprettet!",
"groupPlanUpgraded": "<strong><%= groupName %></strong> blev opgraderet til en Gruppeplan!",
"groupPlanCreated": "<strong><%= groupName %></strong> blev oprettet!",
"onlyGroupLeaderCanInviteToGroupPlan": "Kun gruppelederen kan invitere brugere til en gruppe med et abonnement.",
"paymentDetails": "Betalingsdetaljer",
"aboutToJoinCancelledGroupPlan": "Du er ved at slutte dig til en gruppe med en opsagt gruppeplan. Du vil IKKE få et gratis abonnement.",
@@ -321,7 +321,7 @@
"allAssignedCompletion": "All - Completes when all assigned users finish",
"pmReported": "Tak, fordi du rapporterede denne besked.",
"features": "Funktioner",
"invitedToPartyBy": "<a href=\"/profile/<%- userId %>\" target=\"_blank\">@<%- userName %></a> har inviteret dig til holdet <span class=\"notification-bold\"><%- party %></span>",
"invitedToPartyBy": "<a href=\"/profile/<%= userId %>\" target=\"_blank\">@<%= userName %></a> har inviteret dig til holdet <span class=\"notification-bold\"><%= party %></span>",
"PMUserDoesNotReceiveMessages": "Denne bruger modtager ikke længere private beskeder",
"blockedToSendToThisUser": "Du kan ikke sende til denne spiller da du har blokeret denne spiller.",
"blockYourself": "Du kan ikke blokere dig selv",
+2 -3
View File
@@ -82,13 +82,12 @@
"paymentMethods": "Køb med",
"paymentSuccessful": "Din betaling gik igennem!",
"paymentYouReceived": "Du modtog:",
"paymentYouSentGems": "Du sendte <strong><%- name %></strong>:",
"paymentYouSentSubscription": "Du sendte <strong><%- name %></strong> et <%= months %>-måneders Habitica-abonnement.",
"paymentYouSentGems": "Du sendte <strong><%= name %></strong>:",
"paymentYouSentSubscription": "Du sendte <strong><%= name %></strong> et <%= months %>-måneders Habitica-abonnement.",
"paymentSubBilling": "Betalingen for dit abonnement vil blive trukket <strong>$<%= amount %></strong> hver <strong><%= months %> måneder</strong>.",
"success": "Succes!",
"classGear": "Klasseudstyr",
"classGearText": "Tillykke med at vælge klasse! Jeg har tilføjet dit nye basisudstyr til dit inventar. Tag et kig forneden og tag det på!",
"autoAllocate": "Tildel automatisk",
"spells": "Evner",
"skillsTitle": "<%= classStr %> Evner",
"toDo": "To Do",
+2 -2
View File
@@ -66,8 +66,8 @@
"mountNotOwned": "Du ejer ikke dette ridedyr.",
"feedPet": "Giv <%= text %> til din <%= name %>?",
"raisedPet": "Du har opdrættet din/dit <%= pet %>!",
"petName": "<%= potion(locale) %> <%= egg(locale) %>",
"mountName": "<%= potion(locale) %>-<%= mount(locale) %>",
"petName": "<%= potion %> <%= egg %>",
"mountName": "<%= potion %>-<%= mount %>",
"keyToPets": "Nøgle til Kæledyrskennelen",
"keyToPetsDesc": "Sæt alle standardkæledyrene fri, så du kan samle dem igen. (Kæledyr fra quests og sjældne kæledyr påvirkes ikke.)",
"keyToMounts": "Nøgle til Ridedyrskennelen",
+23 -4
View File
@@ -27,8 +27,8 @@
"userData": "Brugerdata",
"exportUserData": "Eksportér brugerdata:",
"export": "Eksportér",
"xml": "(XML)",
"json": "(JSON)",
"xml": "XML",
"json": "JSON",
"customDayStart": "Brugerdefineret starttidspunkt",
"sureChangeCustomDayStartTime": "Er du sikker på, at du vil ændre dit brugerdefinerede starttidspunkt? Dine Daglige vil blive opdateret næste gang du bruger Habitica efter <%= time %>. Vær sikker på, at du har udført dine Daglige før da!",
"customDayStartHasChanged": "Dit brugerdefinerede starttidspunkt er ændret.",
@@ -46,7 +46,7 @@
"newUsername": "Nyt brugernavn",
"dangerZone": "Farezone",
"resetText1": "ADVARSEL! Dette nulstiller mange dele af din konto. Vi fraråder på det kraftigste dette, men nogen finder det brugbart i begyndelsen efter at have eksperimenteret med Habitica i et kort stykke tid.",
"resetText2": "Du vil miste alle niveauer, Guld, og Erfaringspoint. Alle dine Opgaver (bortset fra dem fra Udfordringer) vil blive slettet permanent, og du vil miste al deres historik. Du vil miste alt dit udstyr, undtagen gratis gave-udstyr eller Mystiske abonnentsgenstande. Du vil være i stand til at købe alle de mistede genstand igen, inklusiv tidsbegrænset udstyr (du skal være den korrekte klasse for at købe klasse-begrænset udstyr). Du vil beholde din nuværende klasse, præstationer og dine kæle- og ridedyr. Du ville måske foretrække at bruge en Genfødselskugle i stedet. Det er en meget sikrere mulighed, og du vil beholde dine opgaver og udstyr.",
"resetText2": "En anden mulighed er at bruge en <b>Genfødselskugle</b>, hvilket vil nulstille alt, men du vil beholde dine opgaver og dit udstyr.",
"deleteLocalAccountText": "Er du sikker? Dette vil slette din konto for evigt, og den kan aldrig gendannes! Du skal registrere en ny konto for at kunne bruge Habitica igen. Ædelsten du har brugt eller har på lager vil ikke blive refunderet. Hvis du er fuldstændig sikker, så skriv dit kodeord i boksen herunder.",
"deleteSocialAccountText": "Er du sikker? Dette vil slette din brugerkonto for evigt, og den kan aldrig gendannes! Du vil være nødt til at oprette en ny konto for at bruge Habitica igen. Ædelsten i en Klanbank eller som er blevet brugt vil ikke blive refunderet. Hvis du virkelig er helt sikker, så indtast \"<%= magicWord %>\" i tekstboksen nedenunder.",
"API": "API",
@@ -176,5 +176,24 @@
"account": "Konto",
"loginMethods": "Login metoder",
"character": "Karakter",
"siteLanguage": "Webstedets sprog"
"siteLanguage": "Webstedets sprog",
"showLevelUpModal": "Når man går et level op",
"showHatchPetModal": "Når man udruger et dyr",
"showRaisePetModal": "Når man opgradere et dyr til ridedyr",
"showStreakModal": "Når man modtager en streak præstation",
"baileyAnnouncement": "Seneste Bailey meddelelse",
"view": "Vis",
"feedbackPlaceholder": "Tilføj til feedback",
"downloadCSV": "Download CSV",
"downloadAs": "Download As",
"yourUserData": "Dine brugerdata",
"taskHistory": "Opgave historie",
"resetTextLocal": "Hvis du er helt sikker, indsæt dit kodeord i tekstfeltet nedenfor.",
"yourUserDataDisclaimer": "Her kan du downloade en kopi af din opgavehistorie og dine bruger data.",
"useridCopied": "BrugerID er kopieret til udklipsholderen.",
"developerMode": "Udviklingsmode",
"api": "API",
"currentPass": "Nuværende kodeord",
"resetDetail1": "Du vil miste alle dine levels, dit Guld, og Erfaringspoint.",
"resetDetail2": "Du vil forblive i din valgte klasse, og beholde dine præstationer, dyr og ridedyr."
}
+7 -4
View File
@@ -2,11 +2,10 @@
"challenge": "Herausforderung",
"challengeDetails": "Herausforderungen sind Gemeinschaftsereignisse, an denen Spieler teilnehmen und Preise gewinnen können, indem sie die dazugehörigen Aufgaben erledigen.",
"brokenChaLink": "Toter Herausforderungs-Link",
"brokenTask": "Toter Herausforderungs-Link: Diese Aufgabe war Teil einer Herausforderung, aber ist mittlerweile entfernt worden. Was möchtest Du tun?",
"keepIt": "Behalten",
"removeIt": "Entfernen",
"brokenChallenge": "Toter Herausforderungs-Link: Diese Aufgabe war Teil einer Herausforderung, aber die Herausforderung (oder Gruppe) wurde gelöscht. Was möchtest Du mit den verwaisten Aufgaben tun?",
"challengeCompleted": "Diese Herausforderung ist beendet, und gewonnen hat <span class=\"badge\"><%- user %></span>! Was soll mit den verwaisten Aufgaben geschehen?",
"brokenChallenge": "Defekter Herausforderungslink",
"challengeCompleted": "Herausforderung abgeschlossen!",
"unsubChallenge": "Toter Herausforderungs-Link: Diese Aufgabe war Teil einer Herausforderung, aber Du hast aufgehört an dieser Herausforderungs teilzunehmen. Was soll mit den verwaisten Aufgaben geschehen?",
"challenges": "Herausforderungen",
"endDate": "Endet",
@@ -109,5 +108,9 @@
"cannotClone": "Diese Herausforderung kann nicht dupliziert werden, weil einer oder mehrere Spieler sie als unangemessen gemeldet haben. Einer der Mitarbeiter wird dich in Kürze mit Anweisungen kontaktieren. Wenn mehr als 48 Stunden vergangen sind, und du nichts von ihnen gehört hast, schicke bitte eine Email an admin@habitica.com, um Unterstützung zu erhalten.",
"resetFlags": "Markierungen zurücksetzen",
"messageChallengeFlagOfficial": "Offizielle Herausforderungen können nicht gemeldet werden.",
"deleteChallengeRefundDescription": "Wenn du diese Herausforderung löschst, bekommst du den Preis in Edelsteinen erstattet und die Aufgaben der Herausforderung verbleiben auf der Aufgabentafel der Teilnehmer."
"deleteChallengeRefundDescription": "Wenn du diese Herausforderung löschst, bekommst du den Preis in Edelsteinen erstattet und die Aufgaben der Herausforderung verbleiben auf der Aufgabentafel der Teilnehmer.",
"brokenTaskDescription": "Diese Aufgabe war Teil einer Herausforderung, wurde jedoch daraus entfernt. Was möchtest Du tun?",
"brokenChallengeDescription": "Diese Aufgabe war Teil einer Herausforderung, aber die Herausforderung (oder Gruppe) wurde gelöscht. Was soll mit den verwaisten Aufgaben geschehen?",
"challengeCompletedDescription": "Gewonnen hat <%= user %>! Was soll mit den verwaisten Aufgaben geschehen?",
"brokenTask": "Defekter Link zur Herausforderung"
}
+16 -7
View File
@@ -83,7 +83,7 @@
"allocatePerPop": "Erhöhe Wahrnehmung um einen Punkt",
"allocateInt": "Zugewiesene Intelligenzpunkte:",
"allocateIntPop": "Erhöhe Intelligenz um einen Punkt",
"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!",
"noMoreAllocate": "Nachdem du nun Level 100 erreicht hast, erhältst du keine weiteren Attributpunkte mehr. Du kannst weiter aufsteigen oder mit der <a href=/shops/market>Sphäre der Wiedergeburt</a> ein neues Abenteuer auf Level 1 beginnen.",
"stats": "Attributwerte",
"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.",
@@ -114,12 +114,12 @@
"unallocated": "Freie Attributspunkte",
"autoAllocation": "Verteilungsmuster",
"autoAllocationPop": "Verteilt gemäß Deiner Einstellungen Punkte auf Deine Attribute, wenn Du ein Level aufsteigst.",
"evenAllocation": "Attributspunkte gleichmäßig verteilen",
"evenAllocationPop": "Weist jedem Statuswert die gleiche Anzahl an Punkten zu.",
"evenAllocation": "Gleichmäßig verteilen",
"evenAllocationPop": "Weist jedem Attribut die gleiche Anzahl von Punkten zu.",
"classAllocation": "Punkte anhand der Klasse verteilen",
"classAllocationPop": "Weist den Statuswerten die wichtig für Deine Klasse sind, mehr Punkte zu.",
"taskAllocation": "Verteile Punkte abhängig von Aufgaben-Aktivität",
"taskAllocationPop": "Verteilt Punkte abhängig davon, welche Kategorien die Aufgaben haben, die Du überwiegend erledigst: Stärke, Intelligenz, Ausdauer oder Wahrnehmung.",
"classAllocationPop": "Weist den Attributen die wichtig für Deine Klasse sind, mehr Punkte zu.",
"taskAllocation": "Verteile Punkte abhängig von Aufgabenaktivität",
"taskAllocationPop": "Verteilt Punkte basierend auf den Kategorien Stärke, Intelligenz, Konstitution und Wahrnehmung, die mit den von Dir erledigten Aufgaben verbunden sind.",
"distributePoints": "Verteile freie Punkte automatisch",
"distributePointsPop": "Verteilt alle freien Attributpunkte gemäß Deinem gewählten Verteilungsmuster.",
"warriorText": "Krieger verursachen mehr und stärkere \"kritische Treffer\", die zufällige Boni auf Gold, Erfahrung und Beute beim Erfüllen einer Aufgabe geben. Sie sind auch sehr stark gegen Bossmonster. Spiele einen Krieger, wenn Dich die Chance auf Belohnungen im Lottogewinn-Stil besonders reizt und Du besonders effektiv gegen Bossmonster sein willst!",
@@ -191,5 +191,14 @@
"titleHaircolor": "Haarfarben",
"titleFacialHair": "Bärte",
"titleHairbase": "Frisuren",
"customizations": "Individualisierungen"
"customizations": "Individualisierungen",
"strTaskText": "Erhöht die kritische Trefferchance und den Schaden beim Erfüllen von Aufgaben. Erhöht außerdem den Schaden, der Quest-Bossen zugefügt wird.",
"conTaskText": "Reduziert den Schaden, der durch verpasste tägliche Aufgaben und negative Gewohnheiten verursacht wird. Reduziert nicht den Schaden durch Quest-Bosse.",
"autoAllocate": "Automatische Zuweisung",
"pointsAvailable": "Verfügbare Punkte",
"allocationMethod": "Zuteilungsmethode",
"assignedStat": "Zugewiesener Wert",
"intTaskText": "Erhöht die durch Aufgaben gesammelte Erfahrung. Erhöht außerdem deine Manakapazität und Manaregenerationsrate.",
"perTaskText": "Erhöht die Drop-Chance für Gegenstände, die tägliche Drop-Obergrenze für Gegenstände, die Serienboni für Aufgaben und das beim Abschließen von Aufgaben verdiente Gold.",
"statAllocationInfo": "Mit jedem Level erhältst Du einen Punkt, den Du einem Attribut Deiner Wahl zuweisen kannst. Du kannst die Zuweisung manuell vornehmen oder es dem Spiel überlassen, indem Du eine der Optionen zur automatischen Zuweisung wählst."
}
+3 -3
View File
@@ -182,7 +182,7 @@
"questEggVelociraptorText": "Velociraptor-Haustier",
"questEggVelociraptorMountText": "Velociraptor-Reittier",
"questEggVelociraptorAdjective": "ein cleveres",
"eggNotes": "Finde ein Schlüpfelixier, das Du über dieses Ei gießen kannst, damit ein <%= eggAdjective(locale) %> <%= eggText(locale) %> schlüpfen kann.",
"eggNotes": "Finde ein Schlüpfelixier, das Du über dieses Ei gießen kannst, damit ein <%= eggAdjective %> <%= eggText %> schlüpfen kann.",
"hatchingPotionBase": "Normales",
"hatchingPotionWhite": "Weißes",
"hatchingPotionDesert": "Wüstenfarbenes",
@@ -211,7 +211,7 @@
"hatchingPotionGlow": "Fluoreszierendes",
"hatchingPotionFrost": "Frostiges",
"hatchingPotionIcySnow": "Eisschnee",
"hatchingPotionNotes": "Gieße dies über ein Ei und es wird ein <%= potText(locale) %> Haustier daraus schlüpfen.",
"hatchingPotionNotes": "Gieße dies über ein Ei und es wird ein <%= potText %> Haustier daraus schlüpfen.",
"foodMeat": "Fleisch",
"foodMeatThe": "das Fleisch",
"foodMeatA": "Fleisch",
@@ -406,7 +406,7 @@
"hatchingPotionBalloon": "Ballon",
"wackyPotionAddlNotes": "Kann nicht zum Reittier großgezogen oder für Quest-Haustier Eier benutzt werden.",
"hatchingPotionCryptid": "Kryptisch",
"wackyPotionNotes": "Schütte dies über ein Ei und es wird als Durchgeknalltes <%= potText(locale) %> Haustier schlüpfen.",
"wackyPotionNotes": "Schütte dies über ein Ei und es wird als Durchgeknalltes <%= potText %> Haustier schlüpfen.",
"questEggPlatypusText": "Schnabeltier",
"questEggPlatypusMountText": "Schnabeltier",
"questEggPlatypusAdjective": "ein Perfektionist",
+28 -2
View File
@@ -2800,7 +2800,7 @@
"armorMystery202406Text": "Phantom-Seeräuber Kleidung",
"headMystery202406Text": "Phantom-Seeräuber Hut",
"eyewearMystery202406Text": "Phantom-Seeräuber Maske",
"weaponArmoirePaintbrushNotes": "Ein Ruck purer Inspiration durchdringt dich, wenn du diesen Frabpinsel aufhebst, und ermöglicht dir, alles zu malen, was du dir vorstellen kannst. Erhöht Intelligenz um <%= int %>.Verzauberter Schrank: Malerset (Gegenstand 3 von 4).",
"weaponArmoirePaintbrushNotes": "Ein Ruck purer Inspiration durchdringt Dich, wenn Du diesen Frabpinsel aufhebst, und ermöglicht Dir, alles zu malen, was Du Dir vorstellen kannst. Erhöht Intelligenz um <%= int %>.Verzauberter Schrank: Malerset (Gegenstand 3 von 4).",
"weaponArmoirePaintbrushText": "Farbpinsel",
"weaponArmoireMopText": "Mopp",
"weaponArmoireCleaningClothText": "Putzlappen",
@@ -3444,5 +3444,31 @@
"weaponSpecialWinter2026WarriorNotes": "Sensen helfen beim Schneiden, Ernten und beim Abdecken großer Bereiche - alles Dinge, die du beim Verfeinern von Aufgabenlisten brauchst. Erhöht Stärke um <%= str %>. Limitierte Ausgabe Winterausrüstung 2025-2026.",
"weaponSpecialWinter2026RogueText": "Skistock",
"weaponSpecialWinter2026RogueNotes": "Stistöcke helfen dir dabei, Balance, Stabilität und Timing zu wahren - alles Dinge, die du brauchst, um wirklich produktiv zu sein. Erhöht Stärke um <%= str %>. Limitierte Ausgabe Winterausrüstung 2025-2026.",
"weaponSpecialWinter2026HealerText": "Polarstab"
"weaponSpecialWinter2026HealerText": "Polarstab",
"weaponSpecialWinter2026HealerNotes": "Stäbe dienen als Stütze, Stabilitätshilfe und zur Richtungsfindung - alles Dinge, die dir beim Bezwingen einer Aufgabenliste wirklich helfen. Erhöht Intelligenz um <%= int %>. Limitierte Ausgabe Winterausrüstung 2025-2026.",
"weaponSpecialWinter2026MageNotes": "Kandelaber helfen, indem sie mehrere Kerzen gleichzeitig halten - folge diesem Beispiel, wenn du das nächste Mal multitasken musst. Erhöht Intelligenz um <%= int %> und Wahrnehmung um <%= per %>. Limitierte Ausgabe Winterausrüstung 2025-2026.",
"weaponSpecialWinter2026MageText": "Kandelaber Stab",
"weaponMystery202512Text": "Klinge des Keks-Champions",
"weaponMystery202512Notes": "Ein glänzendes Schwert, gegossen aus Zucker, Minze und arkanen Zaubern. Gewährt keinen Attributbonus. Dezember 2025 Abonnentengegenstand.",
"weaponArmoireBambooFluteNotes": "Hwhoooo! Hu-whooooo! Versammle Deine Gruppe zu einer Meditationssitzung oder einem Selbstfürsorge-Nickerchen, während ihr euch zu den Klängen dieser Bambusflöte entspannt. Erhöht Konstitution und Intelligenz um jeweils <%= attrs %>. Verzauberter Schrank: Musikinstrumentenset 2 (Artikel 2 von 3)",
"armorSpecialWinter2026WarriorNotes": "Eiszapfen brechen und gleiten bei jedem Schritt auf dem Weg zum Abschluss Deiner täglichen Aufgaben. Erhöht die Konstitution um <%= con %>. Limitierte Auflage Winter 2025-2026 Ausrüstung.",
"armorMystery202512Notes": "Bereit für den Kampf mit diesem Schild, das sowohl süß als auch stark ist. Verleiht keinen Vorteil. Dezember 2025 Abonnenten-Gegenstand.",
"armorSpecialWinter2026HealerNotes": "Wie eine natürliche Lichtshow wirst Du auf Deinem Weg zum Abschluss Deiner täglichen Aufgaben umwerfend aussehen. Erhöht die Konstitution um <%= con %>. Limitierte Auflage Winter 2025-2026 Ausrüstung.",
"armorArmoireLoneCowpokeOutfitText": "Outfit eines einsamen Cowboys",
"armorArmoireLoneCowpokeOutfitNotes": "Halt, halt! Möchtest Du ein Zeichen setzen, wenn Du als geheimnisvoller Fremder in die Stadt reitest, bereit, produktiv zu sein? Hier ist das perfekte Outfit, komplett mit Gamaschen und einer glänzenden, silbernen Gürtelschnalle. Erhöht die Konstitution um <%= con %>. Verzauberter Kleiderschrank: Set „Einsamer Cowboy“ (Artikel 2 von 2)",
"headSpecialWinter2026WarriorNotes": "Behalte Deinen Fokus und Deine Konzentration bei, während Du dich in dieser Saison größere Ziele setzen. Erhöht die Kraft um <%= str %>. Limitierte Auflage 2025-2026 Winterausrüstung.",
"weaponMystery202601Text": "Aegis des Winters",
"weaponMystery202601Notes": "Ein eisiger Blasenschild, der magischen Schutz vor gegnerischen Elementen gewährt. Verleiht keinen Vorteil. Januar 2026 Abonnenten-Gegenstand.",
"weaponArmoireBambooFluteText": "Bambusflöte",
"weaponArmoirePrettyPinkParasolText": "Hübscher rosa Sonnenschirm",
"weaponArmoirePrettyPinkParasolNotes": "Hübsch und praktisch ist die beste Kombination. Und für eine besonders beeindruckende Präsentation dreh diesen Sonnenschirm einfach einmal um! Erhöht alle Werte um jeweils <%= attrs %>. Verzauberter Schrank: Pretty in Pink-Set (Artikel 1 von 2)",
"armorSpecialWinter2026WarriorText": "Raureif-Sensenmann-Anzug",
"armorSpecialWinter2026RogueText": "Skianzug und Skier",
"armorSpecialWinter2026RogueNotes": "Rase schnell die Pisten hinunter, um Deine täglichen Aufgaben zu erledigen. Erhöht die Wahrnehmung um <%= pro %>. Limitierte Auflage Winter 2025-2026 Ausrüstung.",
"armorSpecialWinter2026HealerText": "Polar-Mantel",
"armorSpecialWinter2026MageText": "Mitternachtskerzen-Gewand",
"armorSpecialWinter2026MageNotes": "Gleite geschmeidig wie Wachs über Deinen Weg, um Deine täglichen Aufgaben zu erledigen. Erhöht die Intelligenz um <%= int %>. Limitierte Auflage Winter 2025-2026 Ausrüstung.",
"armorMystery202512Text": "Keks-Champion-Rüstung",
"headSpecialWinter2026WarriorText": "Frostsichel-Helm",
"headSpecialWinter2026RogueText": "Skimaske und Schutzbrille"
}
+1 -1
View File
@@ -190,7 +190,7 @@
"messages": "Nachrichten",
"emptyMessagesLine1": "Du hast im Moment keine Nachrichten",
"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!",
"selected": "Ausgewählt",
"howManyToBuy": "Wie viele möchtest Du kaufen?",
+16 -16
View File
@@ -24,14 +24,14 @@
"userId": "Benutzer-ID",
"invite": "Einladen",
"leave": "Verlassen",
"invitedToParty": "Du wurdest in die Party <span class=\"notification-bold\"><%- party %></span> eingeladen",
"invitedToPrivateGuild": "Du wurdest eingeladen, der privaten Gruppe<span class=\"notification-bold\"><%- guild %></span> beizutreten",
"invitedToPublicGuild": "Du wurdest eingeladen, der Gruppe<span class=\"notification-bold-blue\"><%- guild %></span> beizutreten",
"invitedToParty": "Du wurdest in die Party <span class=\"notification-bold\"><%= party %></span> eingeladen",
"invitedToPrivateGuild": "Du wurdest eingeladen, der privaten Gruppe<span class=\"notification-bold\"><%= guild %></span> beizutreten",
"invitedToPublicGuild": "Du wurdest eingeladen, der Gruppe<span class=\"notification-bold-blue\"><%= guild %></span> beizutreten",
"invitationAcceptedHeader": "Deine Einladung wurde angenommen",
"invitationAcceptedBody": "<%= username %> hat Deine Einladung zu <%= groupName %> angenommen!",
"systemMessage": "Systemmeldung",
"newMsgGuild": "<span class=\"notification-bold-blue\"><%- name %></span> hat neue Beiträge",
"newMsgParty": "Deine Party, <span class=\"notification-bold-blue\"><%- name %></span>, hat neue Beiträge",
"newMsgGuild": "<span class=\"notification-bold-blue\"><%= name %></span> hat neue Beiträge",
"newMsgParty": "Deine Party, <span class=\"notification-bold-blue\"><%= name %></span>, hat neue Beiträge",
"chat": "Chat",
"sendChat": "Nachricht senden",
"group": "Gruppe",
@@ -151,14 +151,14 @@
"onlyGroupLeaderCanEditTasks": "Nicht berechtigt, Aufgaben zu bearbeiten!",
"onlyGroupTasksCanBeAssigned": "Nur Team-Aufgaben können verteilt werden",
"assignedTo": "Zugewiesen an",
"assignedToUser": "Zugewiesen: <strong>@<%- userName %></strong>",
"assignedToUser": "Zugewiesen: <strong>@<%= userName %></strong>",
"assignedToMembers": "<%= userCount %> Mitgliedern",
"assignedToYouAndMembers": "<strong>Dir</strong>, <%= userCount %> Mitgliedern",
"youAreAssigned": "Zugewiesen: <strong>Dir</strong>",
"taskIsUnassigned": "Diese Aufgabe ist niemandem zugewiesen",
"confirmUnClaim": "Bist Du sicher, dass Du diese Aufgabe abgeben möchtest?",
"confirmNeedsWork": "Bist Du sicher, dass Du diese Aufgabe auf \"Benötigt Arbeit\" setzen möchtest?",
"userRequestsApproval": "<strong><%- userName %></strong> beantragt eine Bestätigung",
"userRequestsApproval": "<strong><%= userName %></strong> beantragt eine Bestätigung",
"userCountRequestsApproval": "<strong><%= userCount %> Mitglieder</strong> beantragen eine Bestätigung",
"youAreRequestingApproval": "Du beantragst eine Bestätigung",
"chatPrivilegesRevoked": "Du kannst dies nicht tun, da Dir Deine Chat-Privilegien entzogen wurden. Für genauere Angaben oder um zu fragen, ob Dir Deine Chat-Privilegien zurückgegeben werden können, schreibe bitte eine E-Mail an den Community-Manager unter admin@habitica.com oder bitte Deine Eltern diese E-Mail zu schreiben. Bitte gib Deinen @Usernamen in der E-Mail an. Falls ein Moderator Dir bereits mitgeteilt hat, dass diese Sperre zeitlich begrenzt ist, brauchst Du keine E-Mail zu schreiben.",
@@ -168,9 +168,9 @@
"claim": "Aufgabe übernehmen",
"removeClaim": "Aufgabe abtreten",
"onlyGroupLeaderCanManageSubscription": "Nur der Gruppenleiter kann Gruppen-Registrierungen verwalten",
"yourTaskHasBeenApproved": "Deiner Aufgabe <span class=\"notification-green notification-bold\"><%- taskText %></span> wurde Zustimmung erteilt.",
"taskNeedsWork": "<span class=\"notification-bold\"><%- managerName %></span> hat <span class=\"notification-bold\"><%- taskText %></span> als unfertig markiert.",
"userHasRequestedTaskApproval": "<span class=\"notification-bold\"><%- user %></span> bittet um Zustimmung für <span class=\"notification-bold\"><%- taskName %></span>",
"yourTaskHasBeenApproved": "Deiner Aufgabe <span class=\"notification-green notification-bold\"><%= taskText %></span> wurde Zustimmung erteilt.",
"taskNeedsWork": "<span class=\"notification-bold\"><%= managerName %></span> hat <span class=\"notification-bold\"><%= taskText %></span> als unfertig markiert.",
"userHasRequestedTaskApproval": "<span class=\"notification-bold\"><%= user %></span> bittet um Zustimmung für <span class=\"notification-bold\"><%= taskName %></span>",
"approve": "Zustimmen",
"approveTask": "Aufgabe zustimmen",
"needsWork": "Benötigt Arbeit",
@@ -183,8 +183,8 @@
"userIsClamingTask": "`<%= username %> beansprucht:` <%= task %>",
"approvalRequested": "Zustimmung erbeten",
"cantDeleteAssignedGroupTasks": "Du kannst Gruppen-Aufgaben, die Dir zugewiesen wurden, nicht löschen.",
"groupPlanUpgraded": "<strong><%- groupName %></strong> wurde erfolgreich auf einen Gruppenplan hochgestuft!",
"groupPlanCreated": "<strong><%- groupName %></strong> wurde erstellt!",
"groupPlanUpgraded": "<strong><%= groupName %></strong> wurde erfolgreich auf einen Gruppenplan hochgestuft!",
"groupPlanCreated": "<strong><%= groupName %></strong> wurde erstellt!",
"onlyGroupLeaderCanInviteToGroupPlan": "Nur der Gruppenleiter kann Nutzer zu einer Gruppe mit einem Abonnement hinzufügen.",
"paymentDetails": "Zahlungsinformationen",
"aboutToJoinCancelledGroupPlan": "Du bist dabei einer Gruppe mit gekündigtem Plan beizutreten. Du erhältst KEIN freies Abonnement.",
@@ -321,8 +321,8 @@
"allAssignedCompletion": "Alle Ist erledigt, sobald alle zugeteilten Benutzer abschliessen",
"pmReported": "Danke dass Du diese Nachricht gemeldet hast.",
"suggestedGroup": "Vorgeschlagen weil Du bei Habitica neu bist.",
"taskClaimed": "<%- userName %> hat die Aufgabe <span class=\"notification-bold\"><%- taskText %></span> übernommen.",
"youHaveBeenAssignedTask": "<%- managerName %> hat Dir die Aufgabe <span class=\"notification-bold\"><%- taskText %></span> zugeteilt.",
"taskClaimed": "<%= userName %> hat die Aufgabe <span class=\"notification-bold\"><%= taskText %></span> übernommen.",
"youHaveBeenAssignedTask": "<%= managerName %> hat Dir die Aufgabe <span class=\"notification-bold\"><%= taskText %></span> zugeteilt.",
"groupActivityNotificationTitle": "<%= user %> hat in <%= group %> gepostet",
"blockedToSendToThisUser": "Du kannst dieser Person nicht schreiben, weil Du diese Person blockiert hast.",
"PMDisabled": "Private Nachrichten deaktivieren",
@@ -337,7 +337,7 @@
"chooseTeamMember": "Wähle ein Teammitglied",
"unassigned": "Nicht zugewiesen",
"claimRewards": "Belohnung einfordern",
"assignedDateAndUser": "Zugewiesen am <strong><%= date %></strong> von <strong>@<%- username %></strong>",
"assignedDateAndUser": "Zugewiesen am <strong><%= date %></strong> von <strong>@<%= username %></strong>",
"assignedDateOnly": "Zugewiesen am <strong><%= date %></strong>",
"managerNotes": "Manager-Notizen",
"thisTaskApproved": "Dieser Aufgabe wurde zugestimmt",
@@ -399,7 +399,7 @@
"bannedUser": "<strong>Dieser Spieler wurde gebannt.</strong>",
"questWithOthers": "Übernimm Missionen mit anderen",
"sendTotal": "Gesamt:",
"invitedToPartyBy": "<a href=\"/profile/<%- userId %>\" target=\"_blank\">@<%- userName %></a> hat dich eingeladen, der Party beizutreten <span class=\"notification-bold\"><%- party %></span>",
"invitedToPartyBy": "<a href=\"/profile/<%= userId %>\" target=\"_blank\">@<%= userName %></a> hat dich eingeladen, der Party beizutreten <span class=\"notification-bold\"><%= party %></span>",
"partyExceedsInvitesLimit": "Eine Party kann nur bis zu <%= maxInvites %> ausstehende Einladungen haben.",
"lookForParty": "Suche eine Party",
"currentlyLookingForParty": "Du suchst nach einer Party!",
+3 -4
View File
@@ -82,13 +82,12 @@
"paymentMethods": "Kauf mit",
"paymentSuccessful": "Die Zahlung war erfolgreich!",
"paymentYouReceived": "Du hast erhalten:",
"paymentYouSentGems": "Du hast <strong><%- name %></strong> geschickt:",
"paymentYouSentSubscription": "Du hast <strong><%- name %></strong><br> ein <%= months %>-Monate-Abo für Habitica geschickt.",
"paymentYouSentGems": "Du hast <strong><%= name %></strong> geschickt:",
"paymentYouSentSubscription": "Du hast <strong><%= name %></strong><br> ein <%= months %>-Monate-Abo für Habitica geschickt.",
"paymentSubBilling": "Dein Abonnement wird mit <strong>$<%= amount %></strong> alle <strong><%= months %> Monate</strong> verrechnet.",
"success": "Erfolg!",
"classGear": "Klassenausrüstung",
"classGearText": "Glückwunsch zur Wahl Deiner Klasse! Ich habe Deine neue Basiswaffe dem Inventar hinzugefügt. Schaue sie Dir unten an, um sie auszurüsten!",
"autoAllocate": "Automatische Verteilung",
"spells": "Fertigkeiten",
"skillsTitle": "<%= classStr %> Fertigkeiten",
"toDo": "To-Do",
@@ -130,5 +129,5 @@
"sellItems": "Items verkaufen",
"customizationsShopText": "Willst du deinen Style ändern? Hier bist du richtig! Wir haben die frischesten Looks, passend zur Saison, auf Lager.",
"notAvailable": "Dieser Gegenstand ist nicht verfügbar.",
"paymentYouSentSubscriptionG1G1": "Du hast <strong><%- name %></strong><br> ein <%= months %>-Monat(e)-Abo für Habitica geschickt und dasselbe Abo wurde deinem Account im Zuge der \"Schenk' Eins, Bekomm' Eins\"-Aktion gutgeschrieben!"
"paymentYouSentSubscriptionG1G1": "Du hast <strong><%= name %></strong><br> ein <%= months %>-Monat(e)-Abo für Habitica geschickt und dasselbe Abo wurde deinem Account im Zuge der \"Schenk' Eins, Bekomm' Eins\"-Aktion gutgeschrieben!"
}
+2 -2
View File
@@ -66,8 +66,8 @@
"mountNotOwned": "Du besitzt dieses Reittier nicht.",
"feedPet": "<%= text %> an <%= name %> verfüttern?",
"raisedPet": "Du hast ein <%= pet %> aufgezogen!",
"petName": "<%= potion(locale) %> <%= egg(locale) %>",
"mountName": "<%= potion(locale) %> <%= mount(locale) %>",
"petName": "<%= potion %> <%= egg %>",
"mountName": "<%= potion %> <%= mount %>",
"keyToPets": "Schlüssel zu den Haustier-Zwingern",
"keyToPetsDesc": "Lässt alle Standard-Haustiere frei, so dass Du sie erneut sammeln kannst. (Quest- und seltene Haustiere sind nicht betroffen.)",
"keyToMounts": "Schlüssel zu den Reittier-Zwingern",
-1
View File
@@ -123,7 +123,6 @@
"errorTemporaryItem": "Dieser Gegenstand ist nur temporär verfügbar und kann nicht gepinnt werden.",
"addNotes": "Notizen hinzufügen",
"addATitle": "Überschrift hinzufügen",
"sureDeleteType": "Möchtest Du diese <%= type %> wirklich löschen?",
"deleteTaskType": "Diese <%= type %> löschen",
"pressEnterToAddTag": "Drücke Enter um das Tag '<%= tagName %>' hinzuzufügen",
"enterTag": "Gib ein Tag ein",
@@ -1063,6 +1063,18 @@
"backgroundElegantPalaceText": "Elegant Palace",
"backgroundElegantPalaceNotes": "Admire the colorful halls of an Elegant Palace.",
"backgrounds032026": "SET 142: Released March 2026",
"backgroundWaterfallWithRainbowText": "Waterfall with Rainbow",
"backgroundWaterfallWithRainbowNotes": "Admire the breathtaking beauty of a Waterfall with a Rainbow.",
"backgrounds042026": "SET 143: Released April 2026",
"backgroundRidingACometText": "Riding a Comet",
"backgroundRidingACometNotes": "Travel through space while Riding a Comet!",
"backgrounds052026": "SET 144: Released May 2026",
"backgroundElvenCitadelText": "Elven Citadel",
"backgroundElvenCitadelNotes": "Take the scenic journey to an Elven Citadel.",
"timeTravelBackgrounds": "Steampunk Backgrounds",
"backgroundAirshipText": "Airship",
"backgroundAirshipNotes": "Become a sky sailor on board your very own Airship.",
+6 -3
View File
@@ -2,12 +2,15 @@
"challenge": "Challenge",
"challengeDetails": "Challenges are community events in which players compete and earn prizes by completing a group of related tasks.",
"brokenChaLink": "Broken Challenge Link",
"brokenTask": "Broken Challenge Link: this task was part of a challenge, but has been removed from it. What would you like to do?",
"brokenTask": "Broken Challenge Link",
"brokenTaskDescription": "This task was part of a challenge, but has been removed from it. What would you like to do?",
"keepIt": "Keep It",
"removeIt": "Remove It",
"removeTasks": "Remove Tasks",
"brokenChallenge": "Broken Challenge Link: this task was part of a challenge, but the challenge (or group) has been deleted. What to do with the orphan tasks?",
"challengeCompleted": "This challenge has been completed, and the winner was <span class=\"badge\"><%- user %></span>! What to do with the orphan tasks?",
"brokenChallenge": "Broken Challenge Link",
"brokenChallengeDescription": "This task was part of a challenge, but the challenge (or group) has been deleted. What to do with the orphan tasks?",
"challengeCompleted": "Challenge Completed!",
"challengeCompletedDescription": "The winner was <%= user %>! What to do with the orphan tasks?",
"unsubChallenge": "Broken Challenge Link: this task was part of a challenge, but you have unsubscribed from the challenge. What to do with the orphan tasks?",
"challenges": "Challenges",
"endDate": "Ends",
+18 -9
View File
@@ -87,16 +87,20 @@
"allocatePerPop": "Add a Point to Perception",
"allocateInt": "Points allocated to Intelligence:",
"allocateIntPop": "Add a Point to Intelligence",
"noMoreAllocate": "Now that you've hit level 100, you won't gain any more Stat Points. You can continue leveling up, or start a new adventure at level 1 by using the <a href='/shops/market'>Orb of Rebirth</a>!",
"noMoreAllocate": "Now that you've hit level 100, you won't gain any more Stat Points. You can continue leveling up, or start a new adventure at level 1 by using the <a href='/shops/market'>Orb of Rebirth</a>.",
"stats": "Stats",
"strength": "Strength",
"strText": "Strength increases the chance of random \"critical hits\" and the Gold, Experience, and drop chance boost from them. It also helps deal damage to boss monsters.",
"strTaskText": "Increases critical hit chance and damage when scoring tasks. Also increases damage dealt to Quest bosses.",
"constitution": "Constitution",
"conText": "Constitution reduces the damage you take from negative Habits and missed Dailies.",
"conTaskText": "Reduces damage taken from missed Dailies and negative Habits. Does not reduce damage from Quest bosses.",
"perception": "Perception",
"perText": "Perception increases how much Gold you earn, and once you've unlocked the Market, increases the chance of finding items when scoring tasks.",
"perTaskText": "Increases item drop chance, daily item drop cap, task streak bonuses, and Gold earned when completing tasks.",
"intelligence": "Intelligence",
"intText": "Intelligence increases how much Experience you earn, and once you've unlocked Classes, determines your maximum Mana available for class abilities.",
"intTaskText": "Increases Experience earned from tasks. Also increases your Mana cap and Mana regeneration rate.",
"levelBonus": "Level Bonus",
"allocatedPoints": "Allocated Points",
"allocated": "Allocated",
@@ -117,13 +121,14 @@
"levelPopover": "Each level earns you one Point to assign to a Stat of your choice. You can do so manually, or let the game decide for you using one of the Automatic Allocation options.",
"unallocated": "Unallocated Stat Points",
"autoAllocation": "Automatic Allocation",
"autoAllocate": "Auto Allocate",
"autoAllocationPop": "Places Points into Stats according to your preferences, when you level up.",
"evenAllocation": "Distribute Stat Points evenly",
"evenAllocationPop": "Assigns the same number of Points to each Stat.",
"classAllocation": "Distribute Points based on Class",
"classAllocationPop": "Assigns more Points to the Stats important to your Class.",
"taskAllocation": "Distribute Points based on task activity",
"taskAllocationPop": "Assigns Points based on the Strength, Intelligence, Constitution, and Perception categories associated with the tasks you complete.",
"evenAllocation": "Distribute evenly",
"evenAllocationPop": "Assigns the same number of points to each attribute",
"classAllocation": "Distribute based on class",
"classAllocationPop": "Assigns more points to the attributes important to your class",
"taskAllocation": "Distribute based on task activity",
"taskAllocationPop": "Assigns points based on the Strength, Intelligence, Constitution, and Perception categories associated with the tasks you complete",
"distributePoints": "Distribute Unallocated Points",
"distributePointsPop": "Assigns all unallocated Stat Points according to the selected allocation scheme.",
"warriorText": "Warriors score more and better \"critical hits\", which randomly give bonus Gold, Experience, and drop chance for scoring a task. They also deal heavy damage to boss monsters. Play a Warrior if you find motivation from unpredictable jackpot-style rewards, or want to dish out the hurt in boss Quests!",
@@ -190,6 +195,10 @@
"mainHand": "Main-Hand",
"offHand": "Off-Hand",
"statPoints": "Stat Points",
"pts": "pts",
"customizations": "Customizations"
"pointsAvailable": "Points Available",
"allocationMethod": "Allocation Method",
"statAllocationInfo": "Each level earns you one point to assign to a Stat of your choice. You can do so manually, or let the game decide for you using one of the Automatic Allocation options.",
"pts": "PTS",
"customizations": "Customizations",
"assignedStat": "Assigned Stat"
}
+3 -3
View File
@@ -287,7 +287,7 @@
"questEggPlatypusMountText": "Platypus",
"questEggPlatypusAdjective": "a perfectionist",
"eggNotes": "Find a hatching potion to pour on this egg, and it will hatch into <%= eggAdjective(locale) %> <%= eggText(locale) %>.",
"eggNotes": "Find a hatching potion to pour on this egg, and it will hatch into <%= eggAdjective %> <%= eggText %>.",
"hatchingPotionBase": "Base",
"hatchingPotionWhite": "White",
@@ -357,9 +357,9 @@
"hatchingPotionCryptid": "Cryptid",
"hatchingPotionOpal": "Opal",
"hatchingPotionNotes": "Pour this on an egg, and it will hatch as a <%= potText(locale) %> Pet.",
"hatchingPotionNotes": "Pour this on an egg, and it will hatch as a <%= potText %> Pet.",
"premiumPotionUnlimitedNotes": "Not usable on Quest Pet eggs.",
"wackyPotionNotes": "Pour this on an egg, and it will hatch as a Wacky <%= potText(locale) %> Pet.",
"wackyPotionNotes": "Pour this on an egg, and it will hatch as a Wacky <%= potText %> Pet.",
"wackyPotionAddlNotes": "Cannot be raised to Mounts or used on Quest Pet eggs.",
"foodMeat": "Meat",
+9
View File
@@ -138,6 +138,15 @@
"faqQuestion68": "How do I prevent losing HP?",
"webFaqAnswer68": "If you find yourself losing HP often, try some of these tips:\n\n- Pause your Dailies. The \"Pause Damage\" button in Settings will prevent you from losing HP for missed Dailies.\n- Adjust the schedule of your Dailies. By setting them to never be due, you can still complete them for rewards without risking HP loss.\n- Try using class skills:\n\t- Rogues can cast Stealth to prevent damage from missed Dailies\n\t- Warriors can cast Brutal Smash to reduce a Daily's redness, lowering damage taken if missed\n\t- Healers can cast Searing Brightness to reduce Dailies' redness, lowering damage taken if missed",
"faqQuestion69": "What are character stats?",
"webFaqAnswer69": "All players have four character stats that provide different benefits:\n\n* Strength - Increases critical hit chance and damage when scoring tasks. Also increases damage dealt to Quest bosses.\n* Intelligence - Increases Experience earned from tasks. Also increases your Mana cap and Mana regeneration rate.\n* Constitution - Reduces damage taken from missed Dailies and negative Habits. Does not reduce damage from Quest bosses.\n* Perception - Increases item drop chance, daily item drop cap, task streak bonuses, and Gold earned when completing tasks.\n\nStats can be increased through stat point allocation, Equipment, class skills, and leveling up. You also gain one bonus point to all stats every two levels, up to level 100.",
"faqQuestion70": "What are stat points?",
"webFaqAnswer70": "Stat points let you increase your character's core stats. You earn one stat point each time you level up (up to level 100), which you can assign manually or automatically using the Automatic Allocation feature. Stat allocation unlocks with the Class System at level 10.",
"faqQuestion71": "How does Automatic Allocation work?",
"webFaqAnswer71": "The Automatic Allocation feature automatically assigns stat points according to one of the following distribution methods:\n\n* Distribute evenly - Assigns the same number of points to each attribute\n* Distribute based on class - Assigns more points to the attributes important to your class\n* Distribute based on task activity - Assigns points based on Strength, Intelligence, Constitution, and Perception categories associated with the tasks you complete\n\nIf you choose not to use Automatic Allocation, you can manually assign your stat points from the Stats section.",
"iosFaqStillNeedHelp": "If you have a question that isn't on this list or on the [Wiki FAQ](https://habitica.fandom.com/wiki/FAQ), use the Ask a Question form [LINK NEEDED]! We're happy to help.",
"androidFaqStillNeedHelp": "If you have a question that isn't on this list or on the [Wiki FAQ](https://habitica.fandom.com/wiki/FAQ), use the Ask a Question form [LINK NEEDED]! We're happy to help.",
"webFaqStillNeedHelp": "If you have a question that isn't on this list or on the [Wiki FAQ](https://habitica.fandom.com/wiki/FAQ), use the Ask a Question form [LINK NEEDED]! We're happy to help.",
+59 -1
View File
@@ -578,6 +578,15 @@
"weaponSpecialWinter2026MageText": "Candelabra Staff",
"weaponSpecialWinter2026MageNotes": "Candelabras help by holding multiple candles at a time—follow its lead the next time you need to multitask. Increases Intelligence by <%= int %> and Perception by <%= per %>. Limited Edition Winter 2025-2026 Gear.",
"weaponSpecialSpring2026WarriorText": "Mighty Froggy Foil",
"weaponSpecialSpring2026WarriorNotes": "An opportunity to duel might present itself at any moment, and with this formidable foil, you will be ready! Increases Strength by <%= str %>. Limited Edition Spring 2026 Gear.",
"weaponSpecialSpring2026RogueText": "Spring Branch",
"weaponSpecialSpring2026RogueNotes": "An opportunity to grow is nearly upon you, and with these budding branches, you will be ready! Increases Strength by <%= str %>. Limited Edition Spring 2026 Gear.",
"weaponSpecialSpring2026HealerText": "Snowdrop Staff",
"weaponSpecialSpring2026HealerNotes": "An opportunity to begin anew with a fresh start is right up ahead, and with this splendid staff, you will be ready! Increases Intelligence by <%= int %>. Limited Edition Spring 2026 Gear.",
"weaponSpecialSpring2026MageText": "Maypole Parasol",
"weaponSpecialSpring2026MageNotes": "An opportunity to celebrate approaches, and with this pretty parasol pole, you will be ready! Increases Intelligence by <%= int %> and Perception by <%= per %>. Limited Edition Spring 2026 Gear.",
"weaponMystery201411Text": "Pitchfork of Feasting",
"weaponMystery201411Notes": "Stab your enemies or dig in to your favorite foods - this versatile pitchfork does it all! Confers no benefit. November 2014 Subscriber Item.",
"weaponMystery201502Text": "Shimmery Winged Staff of Love and Also Truth",
@@ -626,6 +635,8 @@
"weaponMystery202512Notes": "A shining sword cast from sugar, mint, and arcane enchantments. Confers no benefit. December 2025 Subscriber Item.",
"weaponMystery202601Text": "Winter's Aegis",
"weaponMystery202601Notes": "An icy bubble shield that grants magical protection from opposing elements. Confers no benefit. January 2026 Subscriber Item.",
"weaponMystery202603Text": "Wisteria Wizard Staff",
"weaponMystery202603Notes": "Cast spells to warm the spring air and encourage the blossoms to bud! Confers no benefit. March 2026 Subscriber Item.",
"weaponMystery301404Text": "Steampunk Cane",
"weaponMystery301404Notes": "Excellent for taking a turn about town. March 3015 Subscriber Item. Confers no benefit.",
@@ -1412,6 +1423,15 @@
"armorSpecialWinter2026MageText": "Midwinter Candle Robe",
"armorSpecialWinter2026MageNotes": "Glide smoothly along your path like wax on your way to completing your Dailies. Increases Intelligence by <%= int %>. Limited Edition Winter 2025-2026 Gear.",
"armorSpecialSpring2026WarriorText": "Frog Armor",
"armorSpecialSpring2026WarriorNotes": "Spring into action just as soon as the snow begins to thaw. Increases Constitution by <%= con %>. Limited Edition Spring 2026 Gear.",
"armorSpecialSpring2026RogueText": "Birch Bark Armor",
"armorSpecialSpring2026RogueNotes": "Withstand inevitable spring rains as well as light breezes. Increases Perception by <%= per %>. Limited Edition Spring 2026 Gear.",
"armorSpecialSpring2026HealerText": "Snowdrop Gown",
"armorSpecialSpring2026HealerNotes": "Glide gracefully from a cold, dark winter into glorious spring. Increases Constitution by <%= con %>. Limited Edition Spring 2026 Gear.",
"armorSpecialSpring2026MageText": "Maypole Dancer Outfit",
"armorSpecialSpring2026MageNotes": "Arrive ready to dance, picnic, and enjoy the warm weather spring brings. Increases Intelligence by <%= int %>. Limited Edition Spring 2026 Gear.",
"armorMystery201402Text": "Messenger Robes",
"armorMystery201402Notes": "Shimmering and strong, these robes have many pockets to carry letters. Confers no benefit. February 2014 Subscriber Item.",
"armorMystery201403Text": "Forest Walker Armor",
@@ -1550,6 +1570,8 @@
"armorMystery202509Notes": "Bright silks protect you from the weather, hot or cold. Confers no benefit. September 2025 Subscriber Item.",
"armorMystery202512Text": "Cookie Champion Armor",
"armorMystery202512Notes": "Ready for battle in this plate that is both sweet and strong. Confers no benefit. December 2025 Subscriber Item.",
"armorMystery202604Text": "Audacious Astronaut Spacesuit",
"armorMystery202604Notes": "One small step for your To Do list, one giant leap for your sense of accomplishment! Confers no benefit. April 2026 Subscriber Item.",
"armorMystery301404Text": "Steampunk Suit",
"armorMystery301404Notes": "Dapper and dashing, wot! Confers no benefit. February 3015 Subscriber Item.",
@@ -1800,6 +1822,10 @@
"armorArmoireBlackPartyDressNotes": "Youre strong, smart, hearty, and so fashionable! Increases Strength, Intelligence, and Constitution by <%= attrs %> each. Enchanted Armoire: Black Hairbow Set (Item 2 of 2).",
"armorArmoireLoneCowpokeOutfitText": "Lone Cowpoke Outfit",
"armorArmoireLoneCowpokeOutfitNotes": "Whoa, there! Want to make a statement when you ride into town as a mysterious stranger ready to be productive? Heres the perfect outfit, complete with chaps and a shining, silver belt buckle. Increases Constitution by <%= con %>. Enchanted Armoire: Lone Cowpoke Set (Item 2 of 2)",
"armorArmoireSoftYellowSuitText": "Soft Yellow Suit",
"armorArmoireSoftYellowSuitNotes": "Yellow is an energetic color. Wear this to bed, and you will wake up with the sun the next morning ready to tackle a day full of tasks. Increases Constitution and Strength by <%= attrs %> each. Enchanted Armoire: Yellow Loungewear Set (Item 2 of 3).",
"armorArmoireHandstandOutfitText": "Handstand",
"armorArmoireHandstandOutfitNotes": "Things sure do look different when youre upside-down, dont they? If youre feeling stuck, its time for a fresh perspective! Increases Perception by <%= per %>. Enchanted Armoire: Handstand Set (Item 1 of 1).",
"headgear": "helm",
"headgearCapitalized": "Headgear",
@@ -2352,6 +2378,15 @@
"headSpecialWinter2026MageText": "Midwinter Candle Hat",
"headSpecialWinter2026MageNotes": "Maintain focus and illumination as you set your sights on greater goals this season. Increases Perception by <%= per %>. Limited Edition 2025-2026 Winter Gear.",
"headSpecialSpring2026WarriorText": "Frog Warrior Helm",
"headSpecialSpring2026WarriorNotes": "Frogs are well-known for their resistance to corruption. This helm will grant you their noble qualities! Increases Strength by <%= str %>. Limited Edition Spring 2026 Gear.",
"headSpecialSpring2026RogueText": "Spring Branch Helm",
"headSpecialSpring2026RogueNotes": "Make a striking statement with twigs and buds growing wild in all directions. Increases Perception by <%= per %>. Limited Edition Spring 2026 Gear.",
"headSpecialSpring2026HealerText": "Snowdrop Helm",
"headSpecialSpring2026HealerNotes": "Make a hopeful statement with these beautiful, resilient petals. Increases Intelligence by <%= int %>. Limited Edition Spring 2026 Gear.",
"headSpecialSpring2026MageText": "Mayflower Crown",
"headSpecialSpring2026MageNotes": "Make a joyous statement with bright blooms encircling your head. Increases Perception by <%= per %>. Limited Edition Spring 2026 Gear.",
"headSpecialGaymerxText": "Rainbow Warrior Helm",
"headSpecialGaymerxNotes": "In celebration of the GaymerX Conference, this special helmet is decorated with a radiant, colorful rainbow pattern! GaymerX is a game convention celebrating LGTBQ and gaming and is open to everyone.",
@@ -2538,7 +2573,11 @@
"headMystery202512Text": "Cookie Champion Helm",
"headMystery202512Notes": "Gingerbread forged with ancient magic will protect you as long as you can hold off your urge to try a bite! Confers no benefit. December 2025 Subscriber Item.",
"headMystery202602Text": "Sakura Fox Ears",
"headMystery202602Notes": " Your hearing will be sharpened by these ears such that you can hear the buds of blossoms growing on tree branches as spring approaches. Confers no benefit. February 2026 Subscriber Item.",
"headMystery202602Notes": "Your hearing will be sharpened by these ears such that you can hear the buds of blossoms growing on tree branches as spring approaches. Confers no benefit. February 2026 Subscriber Item.",
"headMystery202603Text": "Wisteria Wizard Hat",
"headMystery202603Notes": "This jaunty hat not only enhances your magical ability, it also has a lovely spring scent! Confers no benefit. March 2026 Subscriber Item.",
"headMystery202604Text": "Audacious Astronaut Helmet",
"headMystery202604Notes": "In space, no one can hear you check off your To Dos. But the real reward is your sense of personal accomplishment! Confers no benefit. April 2026 Subscriber Item.",
"headMystery301404Text": "Fancy Top Hat",
"headMystery301404Notes": "A fancy top hat for the finest of gentlefolk! January 3015 Subscriber Item. Confers no benefit.",
@@ -2769,6 +2808,10 @@
"headArmoireBlacksmithsGogglesNotes": "Shatter and heat-resistant ocular protection is yours when youre working in a forge. Increases Perception by <%= per %>. Enchanted Armoire: Blacksmith Set (Item 1 of 3).",
"headArmoireLoneCowpokeHatText": "Lone Cowpoke Hat",
"headArmoireLoneCowpokeHatNotes": "Howdy there, pardner! Dyou hate when youre out on the range, workin on tasks, and sun gets in your eyes? Well, good thing youve got a hat for that now. Increases Perception by <%= per %>. Enchanted Armoire: Lone Cowpoke Set (Item 1 of 2)",
"headArmoireFloppyYellowHatText": "Yellow Floppy Hat",
"headArmoireFloppyYellowHatNotes": "Many spells have been sewn into this simple hat, giving it a youthful yellow color. Increases all stats by <%= attrs %> each. Enchanted Armoire: Yellow Loungewear Set (Item 1 of 3).",
"headArmoireVerdantArmingCapText": "Verdant Page Arming Cap",
"headArmoireVerdantArmingCapNotes": "This comfy, cushioned coif makes you battle-ready and helps you withstand anything heavy that could come your way. Increases Perception and Constitution by <%= attrs %> each. Enchanted Armoire: Verdant Page Set (Item 1 of 2).",
"offhand": "off-hand item",
"offHandCapitalized": "Off-Hand Item",
@@ -3086,6 +3129,13 @@
"shieldSpecialWinter2026HealerText": "Starburst",
"shieldSpecialWinter2026HealerNotes": "Stars help with wayfinding, energy, and illumination—all things that help you better conquer a task list. Increases Constitution by <%= con %>. Limited Edition Winter 2025-2026 Gear.",
"shieldSpecialSpring2026WarriorText": "Frog Warrior Candelabra",
"shieldSpecialSpring2026WarriorNotes": "Not only can this candelabra light your way, you can use it to melt any lingering snow and ice. Increases Constitution by <%= con %>. Limited Edition Spring 2026 Gear.",
"shieldSpecialSpring2026RogueText": "Spring Branch",
"shieldSpecialSpring2026RogueNotes": "Reach out and reach high with these branches. They double as a back scratcher in a pinch. Increases Strength by <%= str %>. Limited Edition Spring 2026 Gear.",
"shieldSpecialSpring2026HealerText": "Snowdrop Leaf",
"shieldSpecialSpring2026HealerNotes": "Create a light breeze with this fan as the days grow warmer. It doubles as a writing utensil in a pinch. Increases Constitution by <%= con %>. Limited Edition Spring 2026 Gear.",
"shieldMystery201601Text": "Resolution Slayer",
"shieldMystery201601Notes": "This blade can be used to parry away all distractions. Confers no benefit. January 2016 Subscriber Item.",
"shieldMystery201701Text": "Time-Freezer Shield",
@@ -3116,6 +3166,8 @@
"shieldMystery202508Notes": "If you thought one spinning blade was cool looking, try two! Confers no benefit. August 2025 Subscriber Item.",
"shieldMystery202511Text": "Frost Shield",
"shieldMystery202511Notes": "This rugged shield of icy rock protects you from bad Habits but won't freeze your hands. Confers no benefit. November 2025 Subscriber Item.",
"shieldMystery202605Text": "Nightfall Shield",
"shieldMystery202605Notes": "Let the moons shining light protect you from dangers in the dark. Confers no benefit. May 2026 Subscriber Item.",
"shieldMystery301405Text": "Clock Shield",
"shieldMystery301405Notes": "Time is on your side with this towering clock shield! Confers no benefit. June 3015 Subscriber Item.",
@@ -3298,6 +3350,10 @@
"shieldArmoireDoubleBassNotes": "Bom doo bom brrrr brr brr brrrr! Gather your party for some grounding or dancing as you listen to music on this deep double bass. Increases Constitution and Strength by <%= attrs %> each. Enchanted Armoire: Musical Instrument Set 2 (Item 3 of 3)",
"shieldArmoirePrettyPinkGiftBoxText": "Pretty Pink Present",
"shieldArmoirePrettyPinkGiftBoxNotes": "Is this gift from a dear friend? A caring relative? A true love? A secret admirer? Whoever sent it knows youll be pleased with whats inside. Increases all stats by <%= attrs %> each. Enchanted Armoire: Pretty in Pink Set (Item 2 of 2)",
"shieldArmoireSoftYellowPillowText": "Soft Yellow Pillow",
"shieldArmoireSoftYellowPillowNotes": "The experienced warrior packs a pillow for any expedition. Grow and shine as you consolidate all youve learned during past adventures… even while you nap. Increases Intelligence and Perception by <%= attrs %> each. Enchanted Armoire: Yellow Loungewear Set (Item 3 of 3).",
"shieldArmoireVerdantBannerText": "Verdant Page Banner",
"shieldArmoireVerdantBannerNotes": "Wave your banner high to signal friends its time to rally together! Intelligence by <%= int %>. Enchanted Armoire: Verdant Page Set (Item 2 of 2).",
"back": "Back Accessory",
"backBase0Text": "No Back Accessory",
@@ -3392,6 +3448,8 @@
"backMystery202601Notes": "This mark grants the user control over the elements of the season of cold and frost. Confers no benefit. January 2026 Subscriber Item.",
"backMystery202602Text": "Five Tails of Sakura",
"backMystery202602Notes": "These fluffy tails are the color of cherry blossoms, a reminder that spring is on the way! Confers no benefit. February 2026 Subscriber Item.",
"backMystery202605Text": "Nightfall Nimbus",
"backMystery202605Notes": "A glowing aureole of moonlight and starlight to illuminate the darkest night. Confers no benefit. May 2026 Subscriber Item.",
"backArmoireHarpsichordText": "Harpsichord",
"backArmoireHarpsichordNotes": "Pting! Ptiiing! Gather your party for a dinner or picnic and listen to a tinny melody on this harpsichord. Increases Perception and Intelligence by <%= attrs %> each. Enchanted Armoire: Musical Instrument Set 2 (Item 1 of 3)",
+3 -2
View File
@@ -210,7 +210,7 @@
"emptyMessagesLine1": "You don't have any messages",
"emptyMessagesLine2": "Send a message to start a conversation with your Party members or another Habitica player",
"newMessage": "New Message",
"userSentMessage": "<span class=\"notification-bold\"><%- user %></span> sent you a message",
"userSentMessage": "<span class=\"notification-bold\"><%= user %></span> sent you a message",
"letsgo": "Let's Go!",
"selected": "Selected",
"howManyToBuy": "How many would you like to purchase?",
@@ -242,5 +242,6 @@
"whyReportingPlayerPlaceholder": "Reason for report",
"playerReportModalBody": "You should only report a player who violates the <%= firstLinkStart %>Community Guidelines<%= linkEnd %> and/or <%= secondLinkStart %>Terms of Service<%= linkEnd %>. Submitting a false report is a violation of Habiticas Community Guidelines.",
"targetUserNotExist": "Target User: '<%= userName %>' does not exist.",
"rememberToBeKind": "Please remember to be kind, respectful, and follow the <a href='/static/community-guidelines' target='_blank'>Community Guidelines</a>."
"rememberToBeKind": "Please remember to be kind, respectful, and follow the <a href='/static/community-guidelines' target='_blank'>Community Guidelines</a>.",
"confirmPurchase": "Confirm Purchase"
}
+16 -16
View File
@@ -27,15 +27,15 @@
"userId": "User ID",
"invite": "Invite",
"leave": "Leave",
"invitedToParty": "You were invited to join the Party <span class=\"notification-bold\"><%- party %></span>",
"invitedToPartyBy": "<a href=\"/profile/<%- userId %>\" target=\"_blank\">@<%- userName %></a> has invited you to join the Party <span class=\"notification-bold\"><%- party %></span>",
"invitedToPrivateGuild": "You were invited to join the private Group <span class=\"notification-bold\"><%- guild %></span>",
"invitedToPublicGuild": "You were invited to join the Group <span class=\"notification-bold-blue\"><%- guild %></span>",
"invitedToParty": "You were invited to join the Party <span class=\"notification-bold\"><%= party %></span>",
"invitedToPartyBy": "<a href=\"/profile/<%= userId %>\" target=\"_blank\">@<%= userName %></a> has invited you to join the Party <span class=\"notification-bold\"><%= party %></span>",
"invitedToPrivateGuild": "You were invited to join the private Group <span class=\"notification-bold\"><%= guild %></span>",
"invitedToPublicGuild": "You were invited to join the Group <span class=\"notification-bold-blue\"><%= guild %></span>",
"invitationAcceptedHeader": "Your Invitation has been Accepted",
"invitationAcceptedBody": "<%= username %> accepted your invitation to <%= groupName %>!",
"systemMessage": "System Message",
"newMsgGuild": "<span class=\"notification-bold-blue\"><%- name %></span> has new posts",
"newMsgParty": "Your Party, <span class=\"notification-bold-blue\"><%- name %></span>, has new posts",
"newMsgGuild": "<span class=\"notification-bold-blue\"><%= name %></span> has new posts",
"newMsgParty": "Your Party, <span class=\"notification-bold-blue\"><%= name %></span>, has new posts",
"chat": "Chat",
"sendChat": "Send Chat",
"group": "Group",
@@ -184,7 +184,7 @@
"onlyGroupTasksCanBeAssigned": "Only group tasks can be assigned",
"assignTo": "Assign To",
"assignedTo": "Assigned to",
"assignedToUser": "Assigned: <strong>@<%- userName %></strong>",
"assignedToUser": "Assigned: <strong>@<%= userName %></strong>",
"assignedToMembers": "<%= userCount %> users",
"assignedToYouAndMembers": "<strong>You</strong>, <%= userCount %> users",
"youAreAssigned": "Assigned: <strong>you</strong>",
@@ -193,7 +193,7 @@
"chooseTeamMember": "Search for a team member",
"confirmUnClaim": "Are you sure you want to unclaim this task?",
"confirmNeedsWork": "Are you sure you want to mark this task as needing work?",
"userRequestsApproval": "<strong><%- userName %></strong> requests approval",
"userRequestsApproval": "<strong><%= userName %></strong> requests approval",
"userCountRequestsApproval": "<strong><%= userCount %> members</strong> request approval",
"youAreRequestingApproval": "You are requesting approval",
"chatPrivilegesRevoked": "You cannot do this because your chat privileges have been removed. For details or to ask if your privileges can be returned, please email our Community Manager at admin@habitica.com or ask your parent or guardian to email them. Please include your @Username in the email. If a moderator has already told you that your chat ban is temporary, you do not need to send an email.",
@@ -204,12 +204,12 @@
"removeClaim": "Remove Claim",
"onlyGroupLeaderCanManageSubscription": "Only the group leader can manage the group's subscription",
"onlyPrivateGuildsCanUpgrade": "Only private guilds can be upgraded to a group plan.",
"youHaveBeenAssignedTask": "<%- managerName %> has assigned you the task <span class=\"notification-bold\"><%- taskText %></span>.",
"yourTaskHasBeenApproved": "Your task <span class=\"notification-green notification-bold\"><%- taskText %></span> has been approved.",
"youHaveBeenAssignedTask": "<%= managerName %> has assigned you the task <span class=\"notification-bold\"><%= taskText %></span>.",
"yourTaskHasBeenApproved": "Your task <span class=\"notification-green notification-bold\"><%= taskText %></span> has been approved.",
"thisTaskApproved": "This task was approved",
"taskClaimed": "<%- userName %> has claimed the task <span class=\"notification-bold\"><%- taskText %></span>.",
"taskNeedsWork": "<span class=\"notification-bold\"><%- taskText %></span> was unchecked by <span class=\"notification-bold\">@<%- managerName %></span>. Your rewards for completing the task were reverted.",
"userHasRequestedTaskApproval": "<span class=\"notification-bold\"><%- user %></span> requests approval for <span class=\"notification-bold\"><%- taskName %></span>",
"taskClaimed": "<%= userName %> has claimed the task <span class=\"notification-bold\"><%= taskText %></span>.",
"taskNeedsWork": "<span class=\"notification-bold\"><%= taskText %></span> was unchecked by <span class=\"notification-bold\">@<%= managerName %></span>. Your rewards for completing the task were reverted.",
"userHasRequestedTaskApproval": "<span class=\"notification-bold\"><%= user %></span> requests approval for <span class=\"notification-bold\"><%= taskName %></span>",
"approve": "Approve",
"approveTask": "Approve Task",
"needsWork": "Needs Work",
@@ -222,8 +222,8 @@
"userIsClamingTask": "`<%= username %> has claimed:` <%= task %>",
"approvalRequested": "Approval Requested",
"cantDeleteAssignedGroupTasks": "Can't delete group tasks that are assigned to you.",
"groupPlanUpgraded": "<strong><%- groupName %></strong> was successfully upgraded to a Group Plan!",
"groupPlanCreated": "<strong><%- groupName %></strong> was created!",
"groupPlanUpgraded": "<strong><%= groupName %></strong> was successfully upgraded to a Group Plan!",
"groupPlanCreated": "<strong><%= groupName %></strong> was created!",
"onlyGroupLeaderCanInviteToGroupPlan": "Only the group leader can invite users to a group with a subscription.",
"paymentDetails": "Payment Details",
"aboutToJoinCancelledGroupPlan": "You are about to join a group with a canceled plan. You will NOT receive a free subscription.",
@@ -389,7 +389,7 @@
"groupActivityNotificationTitle": "<%= user %> posted in <%= group %>",
"managerNotes": "Manager's Notes",
"assignedDateOnly": "Assigned on <strong><%= date %></strong>",
"assignedDateAndUser": "Assigned by @<%- username %> on <%= date %>",
"assignedDateAndUser": "Assigned by @<%= username %> on <%= date %>",
"claimRewards": "Claim Rewards",
"dayStart": "<strong>Day start</strong>: <%= startTime %>",
"viewStatus": "Status",
+24 -20
View File
@@ -223,26 +223,30 @@
"fall2024UnderworldSorcerorMageSet": "Underworld Sorceror Set (Mage)",
"fall2024SpaceInvaderHealerSet": "Space Invader Set (Healer)",
"fall2024BlackCatRogueSet": "Black Cat Set (Rogue)",
"winter2025MooseWarriorSet": "Moose Warrior Set",
"winter2025AuroraMageSet": "Aurora Mage Set",
"winter2025StringLightsHealerSet": "String Lights Healer Set",
"winter2025SnowRogueSet": "Snow Rogue Set",
"spring2025SunshineWarriorSet": "Sunshine Warrior Set",
"spring2025CrystalPointRogueSet": "Crystal Point Rogue Set",
"spring2025PlumeriaHealerSet": "Plumeria Healer Set",
"spring2025MantisMageSet": "Mantis Mage Set",
"summer2025ScallopWarriorSet": "Scallop Warrior Set",
"summer2025SquidRogueSet": "Squid Rogue Set",
"summer2025SeaAngelHealerSet": "Sea Angel Healer Set",
"summer2025FairyWrasseMageSet": "Fairy Wrasse Mage Set",
"fall2025SasquatchWarriorSet": "Sasquatch Warrior Set",
"fall2025SkeletonRogueSet": "Skeleton Rogue Set",
"fall2025KoboldHealerSet": "Kobold Healer Set",
"fall2025MaskedGhostMageSet": "Masked Ghost Mage Set",
"winter2026RimeReaperWarriorSet": "Rime Reaper Warrior Set",
"winter2026SkiRogueSet": "Ski Rogue Set",
"winter2026PolarBearHealerSet": "Polar Bear Healer Set",
"winter2026MidwinterCandleMageSet": "Midwinter Candle Mage Set",
"winter2025MooseWarriorSet": "Moose Set (Warrior)",
"winter2025AuroraMageSet": "Aurora Set (Mage)",
"winter2025StringLightsHealerSet": "String Lights Set (Healer)",
"winter2025SnowRogueSet": "Snow Set (Rogue)",
"spring2025SunshineWarriorSet": "Sunshine Set (Warrior)",
"spring2025CrystalPointRogueSet": "Crystal Point Set (Rogue)",
"spring2025PlumeriaHealerSet": "Plumeria Set (Healer)",
"spring2025MantisMageSet": "Mantis Set (Mage)",
"summer2025ScallopWarriorSet": "Scallop Set (Warrior)",
"summer2025SquidRogueSet": "Squid Set (Rogue)",
"summer2025SeaAngelHealerSet": "Sea Angel Set (Healer)",
"summer2025FairyWrasseMageSet": "Fairy Wrasse Set (Mage)",
"fall2025SasquatchWarriorSet": "Sasquatch Set (Warrior)",
"fall2025SkeletonRogueSet": "Skeleton Set (Rogue)",
"fall2025KoboldHealerSet": "Kobold Set (Healer)",
"fall2025MaskedGhostMageSet": "Masked Ghost Set (Mage)",
"winter2026RimeReaperWarriorSet": "Rime Reaper Set (Warrior)",
"winter2026SkiRogueSet": "Ski Set (Rogue)",
"winter2026PolarBearHealerSet": "Polar Bear Set (Healer)",
"winter2026MidwinterCandleMageSet": "Midwinter Candle Set (Mage)",
"spring2026FrogWarriorSet": "Frog Set (Warrior)",
"spring2026BranchRogueSet": "Spring Branch Set (Rogue)",
"spring2026SnowdropHealerSet": "Snowdrop Set (Healer)",
"spring2026MaypoleMageSet": "Maypole Set (Mage)",
"winterPromoGiftHeader": "GIFT A SUBSCRIPTION, GET ONE FREE!",
"winterPromoGiftDetails1": "Until January 6th only, when you gift somebody a subscription, you get the same subscription for yourself for free!",
"winterPromoGiftDetails2": "Please note that if you or your gift recipient already have a recurring subscription, the gifted subscription will only start after that subscription is cancelled or has expired. Thanks so much for your support! <3",
+3 -4
View File
@@ -92,9 +92,9 @@
"paymentMethods": "Purchase using",
"paymentSuccessful": "Your payment was successful!",
"paymentYouReceived": "You received:",
"paymentYouSentGems": "You sent <strong><%- name %></strong>:",
"paymentYouSentSubscription": "You sent <strong><%- name %></strong><br> a <%= months %> month(s) Habitica subscription.",
"paymentYouSentSubscriptionG1G1": "You sent <strong><%- name %></strong><br> a <%= months %> month(s) Habitica subscription, and the same subscription was applied to your account for our Gift One Get One promotion!",
"paymentYouSentGems": "You sent <strong><%= name %></strong>:",
"paymentYouSentSubscription": "You sent <strong><%= name %></strong><br> a <%= months %> month(s) Habitica subscription.",
"paymentYouSentSubscriptionG1G1": "You sent <strong><%= name %></strong><br> a <%= months %> month(s) Habitica subscription, and the same subscription was applied to your account for our Gift One Get One promotion!",
"paymentSubBilling": "Your subscription will be billed <strong>$<%= amount %></strong> every <strong><%= months %> months</strong>.",
"groupsPaymentSubBilling": "Your next billing date is <strong><%= renewalDate %></strong>.",
"paymentSubBillingWithMethod": "Your subscription will be billed<br><strong>$<%= amount %>.00 USD</strong> every <strong><%= months %> months</strong> via <strong><%= paymentMethod %></strong>",
@@ -104,7 +104,6 @@
"success": "Success!",
"classGear": "Class Gear",
"classGearText": "Congratulations on choosing a class! I've added your new basic weapon to your inventory. Take a look below to equip it!",
"autoAllocate": "Auto Allocate",
"spells": "Skills",
"skillsTitle": "<%= classStr %> Skills",
"toDo": "To Do",

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