Compare commits

..

176 Commits

Author SHA1 Message Date
SabreCat 08127cfa67 4.270.0 2023-05-09 10:25:24 -05:00
SabreCat ae9f10420f Merge branch 'sabrecat/link-tweak' into release 2023-05-09 10:25:19 -05:00
SabreCat 26bddb6383 Merge branch 'release' into 2023-05-backgrounds-and-armoire 2023-05-09 10:05:11 -05:00
SabreCat b026daec90 fix(rya): wait for DOM finished before update 2023-05-05 16:27:19 -05:00
SabreCat 49f45d27e3 4.269.1 2023-05-04 16:08:53 -05:00
SabreCat d92a03048b fix(analytics): tweak CTA event 2023-05-04 14:20:49 -05:00
SabreCat 8183699cb7 feat(analytics): differentiate party CTA scenarios 2023-05-03 16:04:22 -05:00
SabreCat 9f9e6c4950 feat(links): skip modal for user's own tasks 2023-05-03 16:00:58 -05:00
CuriousMagpie 9a157168a9 feat(content): add May Backgrounds and Enchanted Armoire items 2023-05-03 12:41:42 -04:00
SabreCat c77dd5f200 fix(analytics): move lfp events out of mounted 2023-05-02 16:52:50 -05:00
SabreCat 13e87b1ea0 fix(tests): linting 2023-05-02 10:11:57 -05:00
SabreCat c2515a4042 4.269.0 2023-05-02 09:51:55 -05:00
SabreCat e31bfdc22b fix(stats): allow negative EXP 2023-05-02 09:51:49 -05:00
SabreCat e9e4265545 Squashed commit of the following:
commit 00affb306655a543f5d29b3af6361e686b577a97
Author: SabreCat <sabe@habitica.com>
Date:   Tue May 2 09:47:25 2023 -0500

    fix(tests): account for invite limit changes

commit 47661117f9fd661b8bc8f63b7cc7c8d5f8fa0fd7
Author: SabreCat <sabe@habitica.com>
Date:   Mon May 1 17:39:29 2023 -0500

    fix(lfp): final polish

commit 6a1e5af1db0dd90be3ced7e223f53c9183a206f5
Merge: 728ed2ddad 9e0777bb42
Author: SabreCat <sabe@habitica.com>
Date:   Mon May 1 16:54:12 2023 -0500

    Merge branch 'release' into sabrecat/party-seeking

commit 728ed2ddad7f0962d28f1ab0a271e3555b19296c
Author: SabreCat <sabe@habitica.com>
Date:   Thu Apr 27 16:51:06 2023 -0500

    fix(lfp): loading layout and page visit event

commit 8a56ab329bff922e05963e3ef78fbc26ff273924
Author: SabreCat <sabe@habitica.com>
Date:   Wed Apr 26 16:54:46 2023 -0500

    fix(faq): copy and style updates

commit 6fd00d7f30150a1802e5a37edbb914ef120caf9a
Author: SabreCat <sabe@habitica.com>
Date:   Fri Apr 21 17:12:52 2023 -0500

    feat(lfp): fixes, analytics, FAQ

commit 4b5d7304ad7cfc5f72320b23456ed2898e53caac
Author: SabreCat <sabe@habitica.com>
Date:   Mon Apr 17 15:13:03 2023 -0500

    fix(lfp): smol tweaks

commit 9a5476a2558eb17a603f4aae1b5b2d35773be8b4
Author: SabreCat <sabe@habitica.com>
Date:   Thu Apr 13 16:03:33 2023 -0500

    feat(lfp): refresh button

commit aa58f5018469f38a9a9d31c3bffa26bb88a8c672
Merge: bbb03d006e c8adf20804
Author: SabreCat <sabe@habitica.com>
Date:   Tue Apr 11 17:44:56 2023 -0500

    Merge branch 'release' into sabrecat/party-seeking

commit bbb03d006e8b122bb7206bdc778a31de422167bb
Author: SabreCat <sabe@habitica.com>
Date:   Tue Apr 4 18:30:50 2023 -0500

    fix(lint): whitespace and const

commit 23683ad29a4cce0b0da061ad6c030982034c0a9c
Author: SabreCat <sabe@habitica.com>
Date:   Tue Apr 4 17:02:57 2023 -0500

    chore(LFP): add analytics
    also re-fix loading state

commit 4477d84f5266c87f5583368029b72153f00f0568
Author: SabreCat <sabe@habitica.com>
Date:   Mon Apr 3 16:24:26 2023 -0500

    fix(LFP): address issues with loading

commit bdc5154f24bb5e50963376c3c0c9cc73c0b05ccc
Merge: 81923eef6f 229ed46425
Author: SabreCat <sabe@habitica.com>
Date:   Mon Apr 3 15:58:12 2023 -0500

    Merge branch 'release' into sabrecat/party-seeking

commit 81923eef6f0c627d079475a28f9d93d8e4628934
Author: SabreCat <sabe@habitica.com>
Date:   Thu Mar 30 16:44:49 2023 -0500

    feat(LFP): release candidate

commit fe1f8939fc6b09d36cfaf0b6e5838df04e41009d
Author: SabreCat <sabe@habitica.com>
Date:   Wed Mar 29 17:35:54 2023 -0500

    WIP(LFP): fixes

commit afc361f5a9f806cbd814ad910d1274e3a6609efd
Merge: d6b5cbdebc 7ede3acd01
Author: SabreCat <sabe@habitica.com>
Date:   Wed Mar 29 16:24:39 2023 -0500

    Merge branch 'release' into sabrecat/party-seeking

commit d6b5cbdebc2829e9325ea57fb5deeccc128c1635
Author: SabreCat <sabe@habitica.com>
Date:   Tue Mar 28 16:13:18 2023 -0500

    WIP(LFP): change copy, add close X, fix API response

commit 4274a4625862351ef0ecf33c8a3249ca5ebec7cb
Author: SabreCat <sabe@habitica.com>
Date:   Mon Mar 27 17:07:36 2023 -0500

    fix(LFP): layout, unset when stopping

commit 95abfcfa5f13c9cce6385206947a47f85b76d11d
Merge: 4a360eedd8 53c536b525
Author: SabreCat <sabe@habitica.com>
Date:   Mon Mar 27 16:32:46 2023 -0500

    Merge branch 'release' into sabrecat/party-seeking

commit 4a360eedd8b9cf41d3a0fe7a4cfaa72c5bd7bd26
Author: SabreCat <sabe@habitica.com>
Date:   Thu Mar 23 16:54:49 2023 -0500

    feat(LFP): completed style and infinite scroll

commit bbc439d9d03c9631a450236eb33af66f0428fa50
Author: SabreCat <sabe@habitica.com>
Date:   Tue Mar 21 10:40:26 2023 -0500

    WIP(LFP): nicer layout, buffs fix

commit 1658688597456663477ab19da61ae1b9bc85cf2a
Merge: 664507434f 027e61a93e
Author: SabreCat <sabe@habitica.com>
Date:   Tue Mar 21 09:29:02 2023 -0500

    Merge branch 'release' into sabrecat/party-seeking

commit 664507434f2f76e6bf3b61cdc9e3daddb81204af
Author: SabreCat <sabe@habitica.com>
Date:   Fri Mar 17 17:13:08 2023 -0500

    WIP(LFP): API and client adjustments

commit 2f0b6f2517f9e2d634cb23ee303cfb4542e998ce
Merge: 0db1704325 a16098ccda
Author: SabreCat <sabe@habitica.com>
Date:   Fri Mar 17 16:45:13 2023 -0500

    Merge branch 'release' into sabrecat/party-seeking

commit 0db1704325c3555f0b5d9c8d1dfc44177e90c093
Author: SabreCat <sabe@habitica.com>
Date:   Mon Mar 13 14:48:10 2023 -0500

    fix(modal): scrollbar for squashed viewports

commit 733c35192e0a4e31e1bebfdd7488cfc1f7587f36
Author: SabreCat <sabe@habitica.com>
Date:   Thu Mar 9 12:51:02 2023 -0600

    WIP(party): seekers functional rough

commit d4b854410b557db26eec6e6a26b6d174c02cee3a
Merge: 7fe919825a 0b6b967753
Author: SabreCat <sabe@habitica.com>
Date:   Thu Mar 9 10:07:28 2023 -0600

    Merge branch 'release' into sabrecat/party-seeking

commit 7fe919825abfb6d518cb93b91f5997d3831bd0b5
Author: SabreCat <sabe@habitica.com>
Date:   Thu Mar 2 14:40:09 2023 -0600

    feat(party): several adjustments to seeking feature

commit c93900efcf925f7aaa4c4cb56b4451f19adfb1b3
Author: SabreCat <sabe@habitica.com>
Date:   Wed Mar 1 20:37:11 2023 -0600

    feat(party): initial Seeking API

commit 8bb784daeceb14c23992a6f3af1054a900fc26c1
Merge: e19a661a21 f327795761
Author: SabreCat <sabe@habitica.com>
Date:   Wed Mar 1 18:58:20 2023 -0600

    Merge branch 'release' into sabrecat/party-seeking

commit e19a661a2163a50307a286379bffb44201ed392e
Author: SabreCat <sabe@habitica.com>
Date:   Fri Feb 24 15:51:42 2023 -0600

    WIP(parties): add seeking flag and modal toggle
2023-05-02 09:51:33 -05:00
SabreCat 9e0777bb42 fix(group-plans): tokenize and update ad text 2023-05-01 16:30:21 -05:00
SabreCat c1532996d8 4.268.1 2023-05-01 10:47:53 -05:00
SabreCat 5aa2d9c68d fix(stats): allow negative HP 2023-05-01 10:47:42 -05:00
SabreCat 6ed5a0f44b 4.268.0 2023-04-28 16:19:07 -05:00
Natalie L 76845f5f20 feat(content): add May subscriber items (#14613)
Co-authored-by: SabreCat <sabe@habitica.com>
2023-04-28 16:18:45 -05:00
SabreCat c931823f62 chore(subproj): update habitica-images 2023-04-28 15:55:06 -05:00
SabreCat 33f0a11f19 4.267.3 2023-04-27 15:35:55 -05:00
SabreCat 6cb3dcd76a fix(fcv): disallow negative values 2023-04-27 15:21:37 -05:00
SabreCat 61e41b539d feat(admin): show 3p note in panel 2023-04-26 16:13:42 -05:00
SabreCat d8cb8869e9 Merge branch 'release' into sabrecat/3p-fixes 2023-04-26 15:41:53 -05:00
SabreCat 57027a1a62 4.267.2 2023-04-25 15:50:55 -05:00
SabreCat 92b4a8029d fix(links): handle meta key 2023-04-25 15:14:59 -05:00
SabreCat 7b4dd36827 4.267.1 2023-04-20 15:30:46 -05:00
SabreCat be65042463 Merge branch 'external-link-modal' into release 2023-04-20 15:30:37 -05:00
SabreCat cccb6a9c02 Merge branch 'develop' into release 2023-04-20 15:30:29 -05:00
SabreCat 0ac2f53405 fix(3p): update timing 1/day at most 2023-04-20 15:19:05 -05:00
Weblate 916c7c49e7 Translated using Weblate (Danish)
Currently translated at 79.2% (321 of 405 strings)

Translated using Weblate (Slovak)

Currently translated at 54.5% (119 of 218 strings)

Translated using Weblate (Slovak)

Currently translated at 67.0% (63 of 94 strings)

Translated using Weblate (Hebrew)

Currently translated at 70.9% (93 of 131 strings)

Translated using Weblate (Slovak)

Currently translated at 80.2% (325 of 405 strings)

Translated using Weblate (Slovak)

Currently translated at 87.5% (7 of 8 strings)

Translated using Weblate (Hebrew)

Currently translated at 45.5% (1281 of 2813 strings)

Translated using Weblate (Slovak)

Currently translated at 87.9% (160 of 182 strings)

Translated using Weblate (Hebrew)

Currently translated at 74.1% (135 of 182 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (376 of 376 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (98 of 98 strings)

Translated using Weblate (Slovak)

Currently translated at 53.2% (410 of 770 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (149 of 149 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (149 of 149 strings)

Translated using Weblate (Malay)

Currently translated at 47.2% (128 of 271 strings)

Translated using Weblate (French)

Currently translated at 100.0% (2813 of 2813 strings)

Translated using Weblate (Hebrew)

Currently translated at 45.2% (1272 of 2813 strings)

Translated using Weblate (Hebrew)

Currently translated at 76.1% (163 of 214 strings)

Translated using Weblate (Hebrew)

Currently translated at 91.9% (137 of 149 strings)

Translated using Weblate (Hebrew)

Currently translated at 87.9% (131 of 149 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Italian)

Currently translated at 98.1% (2760 of 2813 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Hebrew)

Currently translated at 61.0% (133 of 218 strings)

Translated using Weblate (Hebrew)

Currently translated at 64.2% (72 of 112 strings)

Translated using Weblate (Hebrew)

Currently translated at 67.9% (89 of 131 strings)

Translated using Weblate (Hebrew)

Currently translated at 26.1% (71 of 271 strings)

Translated using Weblate (Hebrew)

Currently translated at 45.6% (185 of 405 strings)

Translated using Weblate (Hebrew)

Currently translated at 45.1% (1271 of 2813 strings)

Translated using Weblate (Hebrew)

Currently translated at 82.9% (39 of 47 strings)

Translated using Weblate (Hebrew)

Currently translated at 84.4% (157 of 186 strings)

Translated using Weblate (Ukrainian)

Currently translated at 28.4% (799 of 2813 strings)

Translated using Weblate (Indonesian)

Currently translated at 62.7% (170 of 271 strings)

Translated using Weblate (Hebrew)

Currently translated at 75.1% (112 of 149 strings)

Translated using Weblate (Hebrew)

Currently translated at 73.1% (109 of 149 strings)

Translated using Weblate (Hebrew)

Currently translated at 73.1% (109 of 149 strings)

Translated using Weblate (Hebrew)

Currently translated at 73.1% (109 of 149 strings)

Translated using Weblate (Hebrew)

Currently translated at 69.1% (103 of 149 strings)

Translated using Weblate (Hebrew)

Currently translated at 69.1% (103 of 149 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2809 of 2809 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2809 of 2809 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (149 of 149 strings)

Translated using Weblate (Irish)

Currently translated at 12.5% (14 of 112 strings)

Translated using Weblate (Irish)

Currently translated at 60.4% (1700 of 2813 strings)

Translated using Weblate (Indonesian)

Currently translated at 66.0% (144 of 218 strings)

Translated using Weblate (Irish)

Currently translated at 10.7% (12 of 112 strings)

Translated using Weblate (Irish)

Currently translated at 74.4% (111 of 149 strings)

Translated using Weblate (Irish)

Currently translated at 60.3% (1698 of 2813 strings)

Translated using Weblate (Irish)

Currently translated at 5.3% (6 of 112 strings)

Translated using Weblate (Irish)

Currently translated at 100.0% (1 of 1 strings)

Translated using Weblate (Irish)

Currently translated at 74.4% (111 of 149 strings)

Translated using Weblate (Irish)

Currently translated at 50.0% (4 of 8 strings)

Translated using Weblate (Irish)

Currently translated at 89.2% (191 of 214 strings)

Translated using Weblate (Irish)

Currently translated at 29.6% (16 of 54 strings)

Translated using Weblate (Irish)

Currently translated at 93.3% (14 of 15 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Indonesian)

Currently translated at 95.6% (174 of 182 strings)

Translated using Weblate (Malay)

Currently translated at 48.0% (87 of 181 strings)

Translated using Weblate (Arabic)

Currently translated at 45.0% (27 of 60 strings)

Translated using Weblate (Arabic)

Currently translated at 56.8% (124 of 218 strings)

Translated using Weblate (Arabic)

Currently translated at 81.1% (620 of 764 strings)

Translated using Weblate (Arabic)

Currently translated at 61.4% (75 of 122 strings)

Translated using Weblate (French)

Currently translated at 100.0% (218 of 218 strings)

Translated using Weblate (French)

Currently translated at 100.0% (376 of 376 strings)

Translated using Weblate (French)

Currently translated at 100.0% (222 of 222 strings)

Translated using Weblate (Malay)

Currently translated at 45.0% (122 of 271 strings)

Translated using Weblate (Turkish)

Currently translated at 95.7% (90 of 94 strings)

Translated using Weblate (Turkish)

Currently translated at 95.5% (107 of 112 strings)

Translated using Weblate (Ukrainian)

Currently translated at 27.6% (777 of 2813 strings)

Translated using Weblate (Indonesian)

Currently translated at 68.5% (37 of 54 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (121 of 121 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (376 of 376 strings)

Translated using Weblate (Turkish)

Currently translated at 87.9% (131 of 149 strings)

Translated using Weblate (Malay)

Currently translated at 44.6% (121 of 271 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (376 of 376 strings)

Translated using Weblate (Malay)

Currently translated at 46.6% (28 of 60 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (376 of 376 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (222 of 222 strings)

Translated using Weblate (Ukrainian)

Currently translated at 74.0% (566 of 764 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (376 of 376 strings)

Translated using Weblate (Indonesian)

Currently translated at 94.6% (356 of 376 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (222 of 222 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (376 of 376 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (222 of 222 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (121 of 121 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (376 of 376 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (222 of 222 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (121 of 121 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (149 of 149 strings)

Translated using Weblate (Spanish)

Currently translated at 91.9% (2587 of 2813 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (376 of 376 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (405 of 405 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (271 of 271 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (98 of 98 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (122 of 122 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (221 of 221 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (376 of 376 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (221 of 221 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (376 of 376 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (122 of 122 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (221 of 221 strings)

Translated using Weblate (Belarusian)

Currently translated at 4.0% (6 of 149 strings)

Translated using Weblate (Hungarian)

Currently translated at 55.0% (120 of 218 strings)

Translated using Weblate (Hungarian)

Currently translated at 78.6% (103 of 131 strings)

Translated using Weblate (Hungarian)

Currently translated at 39.8% (108 of 271 strings)

Translated using Weblate (Hungarian)

Currently translated at 74.3% (301 of 405 strings)

Translated using Weblate (Ukrainian)

Currently translated at 27.4% (772 of 2813 strings)

Translated using Weblate (Indonesian)

Currently translated at 61.0% (1718 of 2813 strings)

Translated using Weblate (Hungarian)

Currently translated at 60.3% (1697 of 2813 strings)

Translated using Weblate (Hungarian)

Currently translated at 88.4% (161 of 182 strings)

Translated using Weblate (Hungarian)

Currently translated at 66.6% (40 of 60 strings)

Translated using Weblate (Hungarian)

Currently translated at 73.8% (564 of 764 strings)

Translated using Weblate (Hungarian)

Currently translated at 62.2% (76 of 122 strings)

Translated using Weblate (Hungarian)

Currently translated at 60.4% (90 of 149 strings)

Translated using Weblate (Hungarian)

Currently translated at 57.0% (85 of 149 strings)

Translated using Weblate (Ukrainian)

Currently translated at 27.4% (771 of 2813 strings)

Translated using Weblate (Russian)

Currently translated at 85.2% (104 of 122 strings)

Translated using Weblate (Malay)

Currently translated at 46.4% (52 of 112 strings)

Translated using Weblate (Malay)

Currently translated at 53.5% (412 of 770 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (218 of 218 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (218 of 218 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (375 of 375 strings)

Translated using Weblate (Arabic)

Currently translated at 86.1% (663 of 770 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (149 of 149 strings)

Translated using Weblate (Indonesian)

Currently translated at 81.6% (107 of 131 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (121 of 121 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (122 of 122 strings)

Translated using Weblate (Malay)

Currently translated at 43.5% (118 of 271 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.5% (217 of 218 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (121 of 121 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (375 of 375 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (218 of 218 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Galician)

Currently translated at 63.9% (78 of 122 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (405 of 405 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (271 of 271 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Galician)

Currently translated at 63.9% (78 of 122 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (217 of 217 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (405 of 405 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (218 of 218 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2813 of 2813 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (405 of 405 strings)

Translated using Weblate (Galician)

Currently translated at 73.0% (296 of 405 strings)

Translated using Weblate (Spanish)

Currently translated at 91.9% (2587 of 2813 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (Galician)

Currently translated at 64.6% (262 of 405 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (271 of 271 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (375 of 375 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (271 of 271 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (218 of 218 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (149 of 149 strings)

Translated using Weblate (Galician)

Currently translated at 42.4% (115 of 271 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (149 of 149 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (217 of 217 strings)

Translated using Weblate (Galician)

Currently translated at 40.9% (166 of 405 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (Croatian)

Currently translated at 99.2% (269 of 271 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2809 of 2809 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (121 of 121 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Galician)

Currently translated at 71.5% (133 of 186 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Galician)

Currently translated at 94.3% (202 of 214 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (112 of 112 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (375 of 375 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (375 of 375 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (770 of 770 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Ukrainian)

Currently translated at 73.8% (564 of 764 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (112 of 112 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (98 of 98 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (98 of 98 strings)

Translated using Weblate (Croatian)

Currently translated at 99.2% (764 of 770 strings)

Translated using Weblate (Croatian)

Currently translated at 99.2% (134 of 135 strings)

Translated using Weblate (Croatian)

Currently translated at 92.3% (12 of 13 strings)

Translated using Weblate (Croatian)

Currently translated at 80.7% (617 of 764 strings)

Translated using Weblate (Ukrainian)

Currently translated at 73.6% (563 of 764 strings)

Translated using Weblate (Croatian)

Currently translated at 84.0% (647 of 770 strings)

Translated using Weblate (Croatian)

Currently translated at 83.8% (646 of 770 strings)

Translated using Weblate (Croatian)

Currently translated at 83.7% (645 of 770 strings)

Translated using Weblate (Croatian)

Currently translated at 83.7% (645 of 770 strings)

Translated using Weblate (Croatian)

Currently translated at 83.6% (644 of 770 strings)

Translated using Weblate (Croatian)

Currently translated at 83.5% (643 of 770 strings)

Translated using Weblate (Galician)

Currently translated at 96.7% (59 of 61 strings)

Translated using Weblate (Croatian)

Currently translated at 83.3% (642 of 770 strings)

Translated using Weblate (Croatian)

Currently translated at 83.2% (641 of 770 strings)

Translated using Weblate (Croatian)

Currently translated at 83.1% (640 of 770 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (149 of 149 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Galician)

Currently translated at 71.5% (133 of 186 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (149 of 149 strings)

Translated using Weblate (Galician)

Currently translated at 94.9% (356 of 375 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Galician)

Currently translated at 94.3% (202 of 214 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (149 of 149 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (22 of 22 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (2 of 2 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (1 of 1 strings)

Translated using Weblate (French)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Croatian)

Currently translated at 91.7% (167 of 182 strings)

Translated using Weblate (Croatian)

Currently translated at 87.5% (7 of 8 strings)

Translated using Weblate (Croatian)

Currently translated at 98.6% (147 of 149 strings)

Translated using Weblate (Croatian)

Currently translated at 96.3% (261 of 271 strings)

Translated using Weblate (Croatian)

Currently translated at 87.5% (7 of 8 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Indonesian)

Currently translated at 74.0% (566 of 764 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (121 of 121 strings)

Translated using Weblate (Croatian)

Currently translated at 36.2% (66 of 182 strings)

Translated using Weblate (Croatian)

Currently translated at 75.0% (6 of 8 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Ukrainian)

Currently translated at 73.4% (561 of 764 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (149 of 149 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (121 of 121 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (121 of 121 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (1 of 1 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (1 of 1 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Turkish)

Currently translated at 60.7% (468 of 770 strings)

Translated using Weblate (Malay)

Currently translated at 44.7% (81 of 181 strings)

Translated using Weblate (Indonesian)

Currently translated at 98.3% (119 of 121 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Galician)

Currently translated at 42.3% (326 of 770 strings)

Translated using Weblate (Galician)

Currently translated at 65.7% (502 of 764 strings)

Translated using Weblate (Galician)

Currently translated at 40.9% (166 of 405 strings)

Translated using Weblate (Galician)

Currently translated at 94.9% (356 of 375 strings)

Translated using Weblate (Galician)

Currently translated at 43.6% (336 of 770 strings)

Translated using Weblate (Galician)

Currently translated at 66.6% (509 of 764 strings)

Translated using Weblate (Galician)

Currently translated at 42.4% (172 of 405 strings)

Translated using Weblate (Galician)

Currently translated at 96.8% (363 of 375 strings)

Translated using Weblate (Ukrainian)

Currently translated at 72.9% (557 of 764 strings)

Translated using Weblate (Galician)

Currently translated at 49.4% (381 of 770 strings)

Translated using Weblate (Galician)

Currently translated at 70.6% (540 of 764 strings)

Translated using Weblate (Galician)

Currently translated at 36.1% (98 of 271 strings)

Translated using Weblate (Galician)

Currently translated at 51.3% (208 of 405 strings)

Translated using Weblate (Indonesian)

Currently translated at 64.5% (140 of 217 strings)

Translated using Weblate (Ukrainian)

Currently translated at 26.9% (756 of 2809 strings)

Translated using Weblate (Ukrainian)

Currently translated at 71.5% (547 of 764 strings)

Translated using Weblate (Galician)

Currently translated at 64.2% (36 of 56 strings)

Translated using Weblate (Galician)

Currently translated at 63.3% (83 of 131 strings)

Translated using Weblate (Galician)

Currently translated at 93.3% (56 of 60 strings)

Translated using Weblate (Galician)

Currently translated at 71.5% (133 of 186 strings)

Translated using Weblate (Galician)

Currently translated at 52.7% (406 of 770 strings)

Translated using Weblate (Galician)

Currently translated at 36.8% (80 of 217 strings)

Translated using Weblate (Galician)

Currently translated at 76.9% (10 of 13 strings)

Translated using Weblate (Galician)

Currently translated at 73.2% (560 of 764 strings)

Translated using Weblate (Galician)

Currently translated at 79.7% (75 of 94 strings)

Translated using Weblate (Galician)

Currently translated at 87.5% (7 of 8 strings)

Translated using Weblate (Galician)

Currently translated at 10.0% (15 of 149 strings)

Translated using Weblate (Galician)

Currently translated at 77.0% (47 of 61 strings)

Translated using Weblate (Galician)

Currently translated at 36.5% (99 of 271 strings)

Translated using Weblate (Galician)

Currently translated at 56.2% (228 of 405 strings)

Translated using Weblate (Galician)

Currently translated at 94.3% (202 of 214 strings)

Translated using Weblate (Galician)

Currently translated at 60.7% (1706 of 2809 strings)

Translated using Weblate (Galician)

Currently translated at 98.1% (53 of 54 strings)

Translated using Weblate (Galician)

Currently translated at 93.6% (44 of 47 strings)

Translated using Weblate (Galician)

Currently translated at 43.4% (53 of 122 strings)

Translated using Weblate (Ukrainian)

Currently translated at 26.9% (756 of 2809 strings)

Translated using Weblate (Indonesian)

Currently translated at 94.9% (356 of 375 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Ukrainian)

Currently translated at 71.5% (547 of 764 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (375 of 375 strings)

Translated using Weblate (Galician)

Currently translated at 48.3% (29 of 60 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (112 of 112 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (8 of 8 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (54 of 54 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Malay)

Currently translated at 42.4% (115 of 271 strings)

Translated using Weblate (Ukrainian)

Currently translated at 26.4% (742 of 2809 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (405 of 405 strings)

Translated using Weblate (Ukrainian)

Currently translated at 71.2% (544 of 764 strings)

Translated using Weblate (Spanish)

Currently translated at 92.0% (2587 of 2809 strings)

Translated using Weblate (Ukrainian)

Currently translated at 70.9% (542 of 764 strings)

Translated using Weblate (Indonesian)

Currently translated at 80.1% (105 of 131 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (271 of 271 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (405 of 405 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2809 of 2809 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2809 of 2809 strings)

Translated using Weblate (Ukrainian)

Currently translated at 26.0% (732 of 2809 strings)

Translated using Weblate (Dutch)

Currently translated at 98.3% (120 of 122 strings)

Translated using Weblate (Indonesian)

Currently translated at 95.8% (116 of 121 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.6% (270 of 271 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.8% (2804 of 2809 strings)

Translated using Weblate (Malay)

Currently translated at 42.8% (48 of 112 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.2% (269 of 271 strings)

Translated using Weblate (Indonesian)

Currently translated at 77.5% (314 of 405 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.6% (2799 of 2809 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (15 of 15 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (French)

Currently translated at 100.0% (2809 of 2809 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (770 of 770 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (149 of 149 strings)

Translated using Weblate (Polish)

Currently translated at 87.0% (189 of 217 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (112 of 112 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.5% (267 of 271 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.0% (2782 of 2809 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.0% (2782 of 2809 strings)

Translated using Weblate (Polish)

Currently translated at 96.6% (144 of 149 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2809 of 2809 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2809 of 2809 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (112 of 112 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (405 of 405 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (112 of 112 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (Malay)

Currently translated at 100.0% (149 of 149 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (Indonesian)

Currently translated at 98.1% (210 of 214 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (186 of 186 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (French)

Currently translated at 99.1% (2786 of 2809 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (149 of 149 strings)

Translated using Weblate (French)

Currently translated at 100.0% (271 of 271 strings)

Translated using Weblate (French)

Currently translated at 100.0% (405 of 405 strings)

Translated using Weblate (French)

Currently translated at 99.0% (2783 of 2809 strings)

Translated using Weblate (French)

Currently translated at 100.0% (149 of 149 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.9% (2808 of 2809 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (122 of 122 strings)

Translated using Weblate (Spanish)

Currently translated at 92.9% (252 of 271 strings)

Translated using Weblate (Ukrainian)

Currently translated at 25.0% (704 of 2809 strings)

Translated using Weblate (Dutch)

Currently translated at 88.0% (2474 of 2809 strings)

Translated using Weblate (Ukrainian)

Currently translated at 70.5% (539 of 764 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (122 of 122 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (375 of 375 strings)

Translated using Weblate (Spanish)

Currently translated at 99.7% (768 of 770 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (149 of 149 strings)

Translated using Weblate (Spanish)

Currently translated at 99.5% (220 of 221 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (271 of 271 strings)

Translated using Weblate (Ukrainian)

Currently translated at 67.6% (517 of 764 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (149 of 149 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (405 of 405 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.7% (404 of 405 strings)

Translated using Weblate (Spanish)

Currently translated at 92.0% (2587 of 2809 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (271 of 271 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.8% (2805 of 2809 strings)

Translated using Weblate (Dutch)

Currently translated at 87.3% (2453 of 2809 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (216 of 216 strings)

Translated using Weblate (Indonesian)

Currently translated at 86.8% (53 of 61 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (405 of 405 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.1% (2785 of 2809 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (149 of 149 strings)

Translated using Weblate (Serbian)

Currently translated at 28.1% (42 of 149 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (216 of 216 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Indonesian)

Currently translated at 61.7% (1718 of 2781 strings)

Translated using Weblate (Indonesian)

Currently translated at 66.6% (36 of 54 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (221 of 221 strings)

Translated using Weblate (Indonesian)

Currently translated at 62.9% (34 of 54 strings)

Translated using Weblate (Indonesian)

Currently translated at 62.9% (34 of 54 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Serbian)

Currently translated at 26.1% (39 of 149 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.3% (148 of 149 strings)

Translated using Weblate (Serbian)

Currently translated at 24.8% (37 of 149 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (149 of 149 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (149 of 149 strings)

Co-authored-by: Adrián Chaves Fernández <adrian@chaves.io>
Co-authored-by: Ana Beatriz <anabeatriz.augusto06@yahoo.com>
Co-authored-by: Andressa Murari Sudré <andressa@sudre.com.br>
Co-authored-by: Arthur Ouzlaner <panther1984@gmail.com>
Co-authored-by: Benoit Hetru <me+hbtc@gahanka.net>
Co-authored-by: Delta S <deseji93@gmail.com>
Co-authored-by: Diana <dianazabiyaka@gmail.com>
Co-authored-by: Duygu Erates <duygu.erates@gmail.com>
Co-authored-by: Edward McGibney <edwardmcgibney95@gmail.com>
Co-authored-by: Ellen A M <ellen_a_m@hotmail.com>
Co-authored-by: Ewa Trybuszewski <trybuszewskiewa@gmail.com>
Co-authored-by: Falzart Werefox <muh_fauzi_ramadhan@yahoo.co.id>
Co-authored-by: HURZHII ORYNA <radovaarina@gmail.com>
Co-authored-by: Hanafi <naflizo@gmail.com>
Co-authored-by: Ike Osenberg <ike.osenberg@gmail.com>
Co-authored-by: Jerry Chen <minecjraft@qq.com>
Co-authored-by: Kit Postovnev <sahnonik231@gmail.com>
Co-authored-by: Kovács Máté <kovacsur10@gmail.com>
Co-authored-by: Lauren C <laurenc7834@gmail.com>
Co-authored-by: LiziKnight <liziknight0316@outlook.com>
Co-authored-by: Luna <lunadammandersen@gmail.com>
Co-authored-by: M <maperray@gmail.com>
Co-authored-by: Matúš Goljer <matus.goljer@gmail.com>
Co-authored-by: Michael <mishaopanasuk3@gmail.com>
Co-authored-by: Nazar Paruna <nazarparuna@gmail.com>
Co-authored-by: Ofek yeshurun <ofek.yeshurun@gmail.com>
Co-authored-by: Raithe <RaitheOfDureya@gmail.com>
Co-authored-by: Rara <annisarahmah.xmipa2@gmail.com>
Co-authored-by: Razi H <razi.haj@gmail.com>
Co-authored-by: Sabe Jones <sabe@habitica.com>
Co-authored-by: Sandra Marcial <sandramarcial80@gmail.com>
Co-authored-by: Sciuridae <sweetvshoney@163.com>
Co-authored-by: Stefan Trbojević <garkogidre@gufum.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Yuliia Pastukh <yuliya.bratash666@gmail.com>
Co-authored-by: baozidai <baozidai@outlook.com>
Co-authored-by: burcu atalay <burcuatalay1996@gmail.com>
Co-authored-by: elsaid ata <elsaidata75@gmail.com>
Co-authored-by: fluffstuff <opositesandreality@gmail.com>
Co-authored-by: helio serra carmo junior <theancientguardian@protonmail.com>
Co-authored-by: Коваленко Олексій Володимирович <alex59.kovalenko@gmail.com>
Co-authored-by: Павло Оборін <paka28065@gmail.com>
Co-authored-by: そら <comi4work@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/ar/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/be/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/es/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/ga/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/he/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/id/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/it/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/ms/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/sr/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ar/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/es/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ms/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/challenge/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/character/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/character/he/
Translate-URL: https://translate.habitica.com/projects/habitica/character/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/character/ja/
Translate-URL: https://translate.habitica.com/projects/habitica/character/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/ar/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/es/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/id/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/ru/
Translate-URL: https://translate.habitica.com/projects/habitica/content/es/
Translate-URL: https://translate.habitica.com/projects/habitica/content/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/content/he/
Translate-URL: https://translate.habitica.com/projects/habitica/content/id/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/content/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/content/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/content/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/he/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/death/ga/
Translate-URL: https://translate.habitica.com/projects/habitica/death/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/death/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/ga/
Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/defaulttasks/id/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ar/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/ms/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/front/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/front/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/front/he/
Translate-URL: https://translate.habitica.com/projects/habitica/front/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/front/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/front/id/
Translate-URL: https://translate.habitica.com/projects/habitica/front/ms/
Translate-URL: https://translate.habitica.com/projects/habitica/front/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/front/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/front/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/es/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/ga/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/he/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/id/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/it/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/nl/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/ga/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/he/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/id/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/da/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/he/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/id/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/inventory/ga/
Translate-URL: https://translate.habitica.com/projects/habitica/inventory/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/inventory/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/inventory/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/es/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/he/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/id/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/ms/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/loginincentives/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/merch/ga/
Translate-URL: https://translate.habitica.com/projects/habitica/merch/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/id/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/noscript/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/he/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/id/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/ga/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/he/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/ms/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/tr/
Translate-URL: https://translate.habitica.com/projects/habitica/quests/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/ar/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/id/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/es/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/id/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/settings/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/he/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/ar/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/fr/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/he/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/hu/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/id/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pl/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/sk/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/uk/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/gl/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/hr/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/id/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/zh_Hans/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Challenge
Translation: Habitica/Character
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Contrib
Translation: Habitica/Death
Translation: Habitica/Defaulttasks
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Inventory
Translation: Habitica/Limited
Translation: Habitica/Loginincentives
Translation: Habitica/Merch
Translation: Habitica/Messages
Translation: Habitica/Noscript
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Pets
Translation: Habitica/Quests
Translation: Habitica/Questscontent
Translation: Habitica/Rebirth
Translation: Habitica/Settings
Translation: Habitica/Spells
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2023-04-20 18:55:54 +02:00
SabreCat 3cf5b90f04 fix(3p): bad import, change flag format 2023-04-19 09:33:03 -05:00
SabreCat 86efb02358 fix(api): address issues caused by 3p tools
and flag accounts that use them
2023-04-18 15:43:35 -05:00
SabreCat 164121d9e4 fix(links): handling in RYA 2023-04-17 14:48:55 -05:00
SabreCat a2d209a34b Merge branch 'release' into external-link-modal 2023-04-17 14:41:59 -05:00
SabreCat c8adf20804 4.267.0 2023-04-10 17:09:37 -05:00
Sabe Jones de132c59ea fix(typo): . 2023-04-10 16:54:15 -05:00
SabreCat e5f6c4ba0f fix(links): handle uhuhu 2023-04-10 16:04:46 -05:00
CuriousMagpie 360c17c56e feat(content): add April backgrounds and enchanted armoire item 2023-04-06 15:37:32 -04:00
SabreCat c8b98678d0 Merge branch 'release' into develop 2023-04-05 14:04:45 -05:00
SabreCat ea7e5d2a8d 4.266.0 2023-04-05 14:04:34 -05:00
SabreCat afee09e7cb fix(tz): adjust release timing for DST 2023-04-05 14:02:38 -05:00
CuriousMagpie 01fea6b968 feat(fix): add correct string for potion end date 2023-04-05 12:13:40 -04:00
SabreCat 2df6b6461b fix(links): better http/s handling 2023-04-04 18:25:14 -05:00
CuriousMagpie 8c96ac241a updated stylesheet 2023-04-04 17:42:45 -04:00
CuriousMagpie c0362c614e feat(content): add April fool day potions 2023-04-04 17:14:25 -04:00
SabreCat 229ed46425 fix(tz): dst 2023-04-01 07:08:38 -05:00
SabreCat 7363f08a86 fix(links): handle ext links in profiles 2023-03-29 16:24:06 -05:00
SabreCat 2322f7e342 Merge branch 'release' into external-link-modal 2023-03-29 16:00:53 -05:00
SabreCat 7ede3acd01 feat(event): add AF2023 event const 2023-03-29 15:35:26 -05:00
SabreCat 57f17a08e8 Merge branch 'release' into develop 2023-03-29 15:08:02 -05:00
SabreCat 63453ce01b 4.265.0 2023-03-29 15:07:47 -05:00
SabreCat 888f6f2486 Merge branch 'sabrecat/fools-2023' into release 2023-03-29 15:07:40 -05:00
Natalie L e8501f5cf8 feat(content): Add April 2023 subscriber items (#14562)
* feat(content): add April Subscriber items

* feat(content): add slim armor version to April subscriber items
2023-03-29 14:53:51 -05:00
Natalie L fc49015ff0 chore(content): update press kit faq and third party app list in settings (#14554)
* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (405 of 405 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2781 of 2781 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (217 of 217 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (112 of 112 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (405 of 405 strings)

Translated using Weblate (Portuguese)

Currently translated at 81.7% (331 of 405 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Portuguese)

Currently translated at 62.5% (5 of 8 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (Portuguese)

Currently translated at 63.8% (1777 of 2781 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Portuguese)

Currently translated at 71.6% (43 of 60 strings)

Translated using Weblate (Portuguese)

Currently translated at 79.7% (609 of 764 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (375 of 375 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (770 of 770 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (146 of 146 strings)

Translated using Weblate (German)

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (German)

Currently translated at 79.5% (97 of 122 strings)

Translated using Weblate (German)

Currently translated at 97.7% (753 of 770 strings)

Translated using Weblate (German)

Currently translated at 100.0% (146 of 146 strings)

Translated using Weblate (Portuguese)

Currently translated at 77.0% (94 of 122 strings)

Translated using Weblate (Indonesian)

Currently translated at 76.7% (310 of 404 strings)

Translated using Weblate (Portuguese)

Currently translated at 76.2% (93 of 122 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 85.6% (2381 of 2781 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (217 of 217 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (112 of 112 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 95.8% (732 of 764 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (122 of 122 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (375 of 375 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (770 of 770 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (146 of 146 strings)

Translated using Weblate (Portuguese)

Currently translated at 74.5% (91 of 122 strings)

Translated using Weblate (Portuguese)

Currently translated at 72.1% (88 of 122 strings)

Co-authored-by: Ana Beatriz <anabeatriz.augusto06@yahoo.com>
Co-authored-by: Andressa Murari Sudré <andressa@sudre.com.br>
Co-authored-by: Cachinhos <cachnhos@gmail.com>
Co-authored-by: Falzart Werefox <muh_fauzi_ramadhan@yahoo.co.id>
Co-authored-by: Jay <fallacyofwildlifeconservation@gmail.com>
Co-authored-by: Jerry Chen <minecjraft@qq.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Yu-Wei Tien <a38498987911@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/de/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/de/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/content/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/front/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/de/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/id/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/pt/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Contrib
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Pets
Translation: Habitica/Questscontent
Translation: Habitica/Rebirth
Translation: Habitica/Spells
Translation: Habitica/Subscriber
Translation: Habitica/Tasks

* update press kit and third party app list in settings

---------

Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Ana Beatriz <anabeatriz.augusto06@yahoo.com>
Co-authored-by: Andressa Murari Sudré <andressa@sudre.com.br>
Co-authored-by: Cachinhos <cachnhos@gmail.com>
Co-authored-by: Falzart Werefox <muh_fauzi_ramadhan@yahoo.co.id>
Co-authored-by: Jay <fallacyofwildlifeconservation@gmail.com>
Co-authored-by: Jerry Chen <minecjraft@qq.com>
Co-authored-by: Yu-Wei Tien <a38498987911@gmail.com>
Co-authored-by: SabreCat <sabe@habitica.com>
2023-03-28 16:52:15 -05:00
SabreCat eb4e930e63 fix(ext-links): warn in Party and PMs, env config 2023-03-28 16:39:48 -05:00
SabreCat 53c536b525 4.264.4 2023-03-24 19:46:32 -05:00
SabreCat e2defc675e fix(css): don't make invisible links 2023-03-24 19:46:11 -05:00
SabreCat f0fa2508a9 Merge branch 'release' into develop 2023-03-23 14:35:07 -05:00
SabreCat 232a62ffc7 4.264.3 2023-03-23 14:34:57 -05:00
Phillip Thelen d2d4af227b Fix an issue with gifting subs (#14550)
* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (405 of 405 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2781 of 2781 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (217 of 217 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (112 of 112 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (405 of 405 strings)

Translated using Weblate (Portuguese)

Currently translated at 81.7% (331 of 405 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Portuguese)

Currently translated at 62.5% (5 of 8 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (Portuguese)

Currently translated at 63.8% (1777 of 2781 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Portuguese)

Currently translated at 71.6% (43 of 60 strings)

Translated using Weblate (Portuguese)

Currently translated at 79.7% (609 of 764 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (375 of 375 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (770 of 770 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (146 of 146 strings)

Translated using Weblate (German)

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (German)

Currently translated at 79.5% (97 of 122 strings)

Translated using Weblate (German)

Currently translated at 97.7% (753 of 770 strings)

Translated using Weblate (German)

Currently translated at 100.0% (146 of 146 strings)

Translated using Weblate (Portuguese)

Currently translated at 77.0% (94 of 122 strings)

Translated using Weblate (Indonesian)

Currently translated at 76.7% (310 of 404 strings)

Translated using Weblate (Portuguese)

Currently translated at 76.2% (93 of 122 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 85.6% (2381 of 2781 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (217 of 217 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (112 of 112 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 95.8% (732 of 764 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (122 of 122 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (375 of 375 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (770 of 770 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (146 of 146 strings)

Translated using Weblate (Portuguese)

Currently translated at 74.5% (91 of 122 strings)

Translated using Weblate (Portuguese)

Currently translated at 72.1% (88 of 122 strings)

Co-authored-by: Ana Beatriz <anabeatriz.augusto06@yahoo.com>
Co-authored-by: Andressa Murari Sudré <andressa@sudre.com.br>
Co-authored-by: Cachinhos <cachnhos@gmail.com>
Co-authored-by: Falzart Werefox <muh_fauzi_ramadhan@yahoo.co.id>
Co-authored-by: Jay <fallacyofwildlifeconservation@gmail.com>
Co-authored-by: Jerry Chen <minecjraft@qq.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Yu-Wei Tien <a38498987911@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/de/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/de/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/content/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/front/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/de/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/id/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/pt/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Contrib
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Pets
Translation: Habitica/Questscontent
Translation: Habitica/Rebirth
Translation: Habitica/Spells
Translation: Habitica/Subscriber
Translation: Habitica/Tasks

* gift subscription fix

* remove only

---------

Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Ana Beatriz <anabeatriz.augusto06@yahoo.com>
Co-authored-by: Andressa Murari Sudré <andressa@sudre.com.br>
Co-authored-by: Cachinhos <cachnhos@gmail.com>
Co-authored-by: Falzart Werefox <muh_fauzi_ramadhan@yahoo.co.id>
Co-authored-by: Jay <fallacyofwildlifeconservation@gmail.com>
Co-authored-by: Jerry Chen <minecjraft@qq.com>
Co-authored-by: Yu-Wei Tien <a38498987911@gmail.com>
Co-authored-by: SabreCat <sabe@habitica.com>
2023-03-23 14:34:40 -05:00
SabreCat ca1200b689 feat(links): new external link implementation 2023-03-22 18:06:53 -05:00
SabreCat 008579363c Merge remote-tracking branch 'CuriousMagpie/external-link-modal' into external-link-modal 2023-03-22 16:02:34 -05:00
CuriousMagpie 786b1ec670 add underline to cancel link 2023-03-21 17:07:40 -04:00
SabreCat 573de80a91 Merge remote-tracking branch 'CuriousMagpie/external-link-modal' into external-link-modal 2023-03-21 15:17:21 -05:00
SabreCat 115340e62d Merge branch 'release' into develop 2023-03-21 13:54:35 -05:00
SabreCat 5de2573521 4.264.2 2023-03-21 13:54:23 -05:00
SabreCat b472af532c Merge branch 'apple_sub_fix' into release 2023-03-21 13:54:16 -05:00
CuriousMagpie b264e539f4 add crtl/cmd instructions to skip modal 2023-03-21 14:49:43 -04:00
CuriousMagpie c726208d6e add cancel option 2023-03-21 13:00:15 -04:00
SabreCat b1d2fff13f Merge branch 'release' into develop 2023-03-17 17:25:10 -05:00
SabreCat 027e61a93e 4.264.1 2023-03-17 17:24:56 -05:00
SabreCat c35afb7cfe fix(string): add missing party invite text 2023-03-17 17:24:49 -05:00
SabreCat 8cd706fd95 feat(links): route to selected link 2023-03-17 16:37:16 -05:00
SabreCat 3a4620976e Merge branch 'release' into external-link-modal 2023-03-17 15:59:28 -05:00
SabreCat a16098ccda 4.264.0 2023-03-17 15:09:12 -05:00
SabreCat 118c8421fe fix(tz): account for DST 2023-03-17 15:09:02 -05:00
Natalie L a363e68080 feat(cont): 2023 Spring Fling content (#14525)
* feat(cont): 2023 Spring Fling

* feat(content): 2023 Spring Fling (attempt to add Jungle Buddies Quest Bundle)

* fix(event): various typos and date ranges

* fix(event): correct countdown on Shiny Seed

* fix(quests): correct TypeError in featured list

* feat(content): update Spring gear to canon desc

---------

Co-authored-by: SabreCat <sabe@habitica.com>
2023-03-17 15:05:04 -05:00
SabreCat 1940062200 4.263.1 2023-03-16 16:23:00 -05:00
SabreCat aea075d0bf chore(content): make Jungle Buddies available 2023-03-16 16:22:47 -05:00
Weblate 7388707a43 Merge branch 'origin/develop' into Weblate. 2023-03-16 18:57:12 +01:00
Weblate 597f74c84b Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (405 of 405 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2781 of 2781 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (135 of 135 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (217 of 217 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (56 of 56 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (13 of 13 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (112 of 112 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (131 of 131 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (405 of 405 strings)

Translated using Weblate (Portuguese)

Currently translated at 81.7% (331 of 405 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (214 of 214 strings)

Translated using Weblate (Portuguese)

Currently translated at 62.5% (5 of 8 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (61 of 61 strings)

Translated using Weblate (Portuguese)

Currently translated at 63.8% (1777 of 2781 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Portuguese)

Currently translated at 71.6% (43 of 60 strings)

Translated using Weblate (Portuguese)

Currently translated at 79.7% (609 of 764 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (375 of 375 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (47 of 47 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (770 of 770 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (146 of 146 strings)

Translated using Weblate (German)

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (German)

Currently translated at 79.5% (97 of 122 strings)

Translated using Weblate (German)

Currently translated at 97.7% (753 of 770 strings)

Translated using Weblate (German)

Currently translated at 100.0% (146 of 146 strings)

Translated using Weblate (Portuguese)

Currently translated at 77.0% (94 of 122 strings)

Translated using Weblate (Indonesian)

Currently translated at 76.7% (310 of 404 strings)

Translated using Weblate (Portuguese)

Currently translated at 76.2% (93 of 122 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 85.6% (2381 of 2781 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (60 of 60 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (217 of 217 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (112 of 112 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (267 of 267 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (404 of 404 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (182 of 182 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 95.8% (732 of 764 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (122 of 122 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (375 of 375 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (770 of 770 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (146 of 146 strings)

Translated using Weblate (Portuguese)

Currently translated at 74.5% (91 of 122 strings)

Translated using Weblate (Portuguese)

Currently translated at 72.1% (88 of 122 strings)

Co-authored-by: Ana Beatriz <anabeatriz.augusto06@yahoo.com>
Co-authored-by: Andressa Murari Sudré <andressa@sudre.com.br>
Co-authored-by: Cachinhos <cachnhos@gmail.com>
Co-authored-by: Falzart Werefox <muh_fauzi_ramadhan@yahoo.co.id>
Co-authored-by: Jay <fallacyofwildlifeconservation@gmail.com>
Co-authored-by: Jerry Chen <minecjraft@qq.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Yu-Wei Tien <a38498987911@gmail.com>
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/de/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/achievements/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/de/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/backgrounds/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/de/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/communityguidelines/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/content/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/content/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/contrib/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/faq/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/front/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/front/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/gear/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/generic/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/de/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/id/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hans/
Translate-URL: https://translate.habitica.com/projects/habitica/groups/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/limited/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/messages/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/npc/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/overview/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/pets/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/questscontent/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/rebirth/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/spells/pt/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/pt_BR/
Translate-URL: https://translate.habitica.com/projects/habitica/subscriber/zh_Hant/
Translate-URL: https://translate.habitica.com/projects/habitica/tasks/pt/
Translation: Habitica/Achievements
Translation: Habitica/Backgrounds
Translation: Habitica/Communityguidelines
Translation: Habitica/Content
Translation: Habitica/Contrib
Translation: Habitica/Faq
Translation: Habitica/Front
Translation: Habitica/Gear
Translation: Habitica/Generic
Translation: Habitica/Groups
Translation: Habitica/Limited
Translation: Habitica/Messages
Translation: Habitica/Npc
Translation: Habitica/Overview
Translation: Habitica/Pets
Translation: Habitica/Questscontent
Translation: Habitica/Rebirth
Translation: Habitica/Spells
Translation: Habitica/Subscriber
Translation: Habitica/Tasks
2023-03-16 17:39:07 +01:00
CuriousMagpie 971b124b05 apply correct opacity to close icon 2023-03-15 11:21:25 -04:00
SabreCat de3f1b3f5e chore(sprites): compile 2023-03-14 16:47:14 -05:00
SabreCat 7098d2a72e Merge branch 'release' into sabrecat/fools-2023 2023-03-14 16:46:11 -05:00
SabreCat bbea789700 fix(test): don't cross two month boundaries when anticipating +1 perk 2023-03-14 16:27:09 -05:00
SabreCat 5359a2bf3d fix(lint): === 2023-03-14 16:14:48 -05:00
Phillip Thelen 93e922e774 fix logic 2023-03-14 15:55:41 +01:00
Phillip Thelen f6f1202baf better init for perkMonthCount 2023-03-14 14:37:47 +01:00
SabreCat 83f5c92ff1 fix(lint): loop no likey ++ 2023-03-13 18:36:14 -05:00
negue e39b3bdd35 open the modal by click override 2023-03-11 00:40:05 +01:00
SabreCat a210ab57b0 WIP(external-link): hacky attempt number one 2023-03-10 13:33:51 -06:00
CuriousMagpie 3f3e0e2ae8 modal essentially complete but for scripts 2023-03-08 15:17:15 -05:00
CuriousMagpie 65f12ac9ea adding console log messages 2023-03-08 12:55:58 -05:00
CuriousMagpie d0941810a7 trying to make a modal 2023-03-08 12:52:22 -05:00
CuriousMagpie b77deb28b4 initial commit 2023-03-08 12:01:50 -05:00
SabreCat 99c46602c4 fix(fooling): correct special pet mapping 2023-03-06 09:47:53 -06:00
SabreCat ee585c0ff3 feat(event): April Fools 2023 2023-03-03 14:32:56 -06:00
Phillip Thelen 0754c0ff05 correctly set limit 2023-03-02 18:04:02 +01:00
Phillip Thelen 5d1346e65c fix lint 2023-03-01 15:06:08 +01:00
Phillip Thelen a2ce0ab099 Set request size limit higher, because of iOS receipt sizes 2023-03-01 14:48:50 +01:00
Phillip Thelen 6887fd70c0 try something 2023-02-28 16:17:52 +01:00
Phillip Thelen def24142ca remove wrong test 2023-02-27 13:30:44 +01:00
Phillip Thelen c29049146d prevent sub accidentally also being applied to other account 2023-02-27 12:06:27 +01:00
Phillip Thelen 57fb7ca6f2 fix 2023-02-24 11:25:53 +01:00
Phillip Thelen 62b171ffa5 fix linting 2023-02-24 10:47:50 +01:00
Phillip Thelen be18476292 correctly set perkMonthCount 2023-02-23 13:20:47 +01:00
Phillip Thelen 3f56b7fa3f fix offset calculation 2023-02-17 15:42:31 +01:00
Phillip Thelen d69de2948b fix import 2023-02-16 09:58:09 +01:00
Phillip Thelen c5f5da1d32 Merge remote-tracking branch 'origin/develop' into apple_sub_fix 2023-02-16 09:38:14 +01:00
Phillip Thelen e338fb8ce7 Merge branch 'apple_sub_fix' of https://github.com/HabitRPG/habitica into apple_sub_fix 2023-02-16 09:37:36 +01:00
Phillip Thelen 2d5dcae406 fix tests 2023-02-16 09:37:00 +01:00
Phillip Thelen 3203b09b7a reset perkMonthCount when subscription ends 2023-02-15 10:05:18 +01:00
Phillip Thelen 6ee2e3a379 Merge remote-tracking branch 'origin/develop' into apple_sub_fix 2023-02-13 17:38:14 +01:00
Phillip Thelen 77229f3e5e show year for next hourglass 2023-02-10 17:13:20 +01:00
Phillip Thelen 41cdab1672 adjust tests 2023-02-10 17:13:10 +01:00
Phillip Thelen 58f4dd0c43 fix typo 2023-02-10 17:13:04 +01:00
Phillip Thelen 0ce64a0197 fix next hourglass calculation 2023-02-09 16:20:46 +01:00
Phillip Thelen ad6555c92b fix import 2023-02-06 17:16:43 +01:00
Phillip Thelen c04e8ea514 comma 2023-02-06 13:01:06 +01:00
Phillip Thelen aec2409227 lint fixes 2023-02-06 12:59:54 +01:00
Phillip Thelen 87aebcc19e Merge branch 'apple_sub_fix' of https://github.com/HabitRPG/habitica into apple_sub_fix
# Conflicts:
#	test/api/unit/libs/payments/apple.test.js
#	website/server/libs/payments/subscriptions.js
2023-02-06 11:19:00 +01:00
Phillip Thelen a3bc20f855 Fix case where a number was sometimes a string 2023-02-06 11:17:35 +01:00
Phillip Thelen 86e33b2364 remove log 2023-02-06 11:17:35 +01:00
Phillip Thelen 12479edb77 fix tests and some cases 2023-02-06 11:17:35 +01:00
Phillip Thelen c0c6657536 handle upgrades and creations better 2023-02-06 11:17:35 +01:00
SabreCat e81a052f66 fix(lint): line lengths and so on 2023-02-06 11:17:35 +01:00
Phillip Thelen 82a1d6ff0e Improve handling 2023-02-06 11:17:35 +01:00
Phillip Thelen 0f7001b609 fix lint 2023-02-06 11:17:35 +01:00
Phillip Thelen 87558a325e Handle subscription cancelation better 2023-02-06 11:17:35 +01:00
Phillip Thelen de48925341 remove only 2023-02-06 11:17:35 +01:00
Phillip Thelen 614850e56c fix tests 2023-02-06 11:17:35 +01:00
Phillip Thelen 64a3515c10 Add logic for different types of sub upgrades 2023-02-06 11:17:35 +01:00
Phillip Thelen 8dfa21a4b8 Add field to track when current subscription type started 2023-02-06 11:17:35 +01:00
Phillip Thelen f9a9d4919b Improve recheck handling for test subs 2023-02-06 11:17:35 +01:00
Phillip Thelen ddf1b4060d Better handling for cancellation when user had multiple subs 2023-02-06 11:17:35 +01:00
Phillip Thelen 967717a010 Fix logic for apple subscriptions 2023-02-06 11:17:35 +01:00
SabreCat 9b791b4ba0 fix(test): save user to avoid lock errors 2023-02-06 11:17:35 +01:00
SabreCat 5aca5b4be7 fix(test): linting 2023-02-06 11:17:35 +01:00
Phillip Thelen 0dd25b6431 fix issue where subs would be applied multiple times 2023-02-06 11:17:35 +01:00
Phillip Thelen cf75d941fa fix test 2023-02-06 11:17:35 +01:00
Phillip Thelen 777f7887b4 fix lint errors 2023-02-06 11:17:35 +01:00
Phillip Thelen f07d0f6441 Implement correct handling for when subs are up/downgraded 2023-02-06 11:17:24 +01:00
Phillip Thelen 98ec1757f9 fix tests 2023-02-06 11:16:24 +01:00
SabreCat 742da1f2c6 fix(typo): customER 2023-02-06 11:16:24 +01:00
SabreCat b3d5a8d083 fix(lint): line length 2023-02-06 11:16:24 +01:00
Phillip Thelen b5f2e66025 fix check 2023-02-06 11:16:24 +01:00
Phillip Thelen 9a40674d8d Allow sub upgrades/downgrades on iOS 2023-02-06 11:16:24 +01:00
Phillip Thelen cde5fbef85 Fix case where a number was sometimes a string 2022-12-09 12:16:33 +01:00
Phillip Thelen 8b2af1ef56 remove log 2022-12-05 15:38:30 +01:00
Phillip Thelen 21652c2670 fix tests and some cases 2022-12-02 17:12:17 +01:00
Phillip Thelen d1ee679810 handle upgrades and creations better 2022-12-02 11:51:33 +01:00
SabreCat 67988da33c fix(lint): line lengths and so on 2022-11-30 15:27:53 -06:00
Phillip Thelen fae26a517d Improve handling 2022-11-30 15:06:50 +01:00
Phillip Thelen e3c86349b4 fix lint 2022-11-11 13:58:45 +01:00
Phillip Thelen 6604f38144 Handle subscription cancelation better 2022-11-11 13:54:17 +01:00
Phillip Thelen 037882b50a remove only 2022-11-10 13:55:58 +01:00
Phillip Thelen 15deb778fd fix tests 2022-11-10 13:48:58 +01:00
Phillip Thelen 7d2529f5e1 Add logic for different types of sub upgrades 2022-11-09 19:49:53 +01:00
Phillip Thelen 8d732c59c4 Add field to track when current subscription type started 2022-11-08 12:38:24 +01:00
Phillip Thelen 3a34aa4cc5 Improve recheck handling for test subs 2022-11-08 12:19:17 +01:00
Phillip Thelen e7fc7feddd Better handling for cancellation when user had multiple subs 2022-11-04 13:30:50 +01:00
Phillip Thelen 7fd899b642 Fix logic for apple subscriptions 2022-11-03 17:48:36 +01:00
SabreCat 36d2ad6b9b fix(test): save user to avoid lock errors 2022-11-02 15:15:28 -05:00
SabreCat 164dbdcf10 Merge branch 'develop' into apple_sub_fix 2022-11-02 15:00:24 -05:00
SabreCat b65fa941b9 fix(test): linting 2022-11-02 14:44:49 -05:00
Phillip Thelen ab953440e3 fix issue where subs would be applied multiple times 2022-11-02 16:36:09 +01:00
Phillip Thelen 1143f690d1 fix test 2022-10-28 15:28:46 +02:00
Phillip Thelen 08469c556b fix lint errors 2022-10-28 12:41:43 +02:00
Phillip Thelen 13a25ad89e Implement correct handling for when subs are up/downgraded 2022-10-28 11:23:48 +02:00
Phillip Thelen 8e2e170930 fix tests 2022-10-26 16:30:35 +02:00
SabreCat e6a7d15644 fix(typo): customER 2022-10-25 16:59:59 -05:00
SabreCat 6a4b08203f fix(lint): line length 2022-10-25 16:52:16 -05:00
SabreCat c9016c8d42 Merge branch 'develop' into phillip/sub_change 2022-10-25 16:47:51 -05:00
Phillip Thelen 31685c3e94 fix check 2022-10-21 16:59:18 +02:00
Phillip Thelen c25b09c7ed Allow sub upgrades/downgrades on iOS 2022-10-21 16:57:12 +02:00
360 changed files with 8834 additions and 3768 deletions
+2 -1
View File
@@ -86,5 +86,6 @@
"RATE_LIMITER_ENABLED": "false",
"REDIS_HOST": "aaabbbcccdddeeefff",
"REDIS_PORT": "1234",
"REDIS_PASSWORD": "12345678"
"REDIS_PASSWORD": "12345678",
"TRUSTED_DOMAINS": "https://localhost,https://habitica.com"
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "habitica",
"version": "4.263.0",
"version": "4.270.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "habitica",
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
"version": "4.263.0",
"version": "4.270.0",
"main": "./website/server/index.js",
"dependencies": {
"@babel/core": "^7.20.12",
+100 -21
View File
@@ -231,13 +231,16 @@ describe('cron', async () => {
},
});
// user1 has a 1-month recurring subscription starting today
user1.purchased.plan.customerId = 'subscribedId';
user1.purchased.plan.dateUpdated = moment().toDate();
user1.purchased.plan.planId = 'basic';
user1.purchased.plan.consecutive.count = 0;
user1.purchased.plan.consecutive.offset = 0;
user1.purchased.plan.consecutive.trinkets = 0;
user1.purchased.plan.consecutive.gemCapExtra = 0;
beforeEach(async () => {
user1.purchased.plan.customerId = 'subscribedId';
user1.purchased.plan.dateUpdated = moment().toDate();
user1.purchased.plan.planId = 'basic';
user1.purchased.plan.consecutive.count = 0;
user1.purchased.plan.perkMonthCount = 0;
user1.purchased.plan.consecutive.offset = 0;
user1.purchased.plan.consecutive.trinkets = 0;
user1.purchased.plan.consecutive.gemCapExtra = 0;
});
it('does not increment consecutive benefits after the first month', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(1, 'months')
@@ -271,6 +274,24 @@ describe('cron', async () => {
expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(0);
});
it('increments consecutive benefits after the second month if they also received a 1 month gift subscription', async () => {
user1.purchased.plan.perkMonthCount = 1;
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(2, 'months')
.add(2, 'days')
.toDate());
// Add 1 month to simulate what happens a month after the subscription was created.
// Add 2 days so that we're sure we're not affected by any start-of-month effects
// e.g., from time zone oddness.
await cron({
user: user1, tasksByType, daysMissed, analytics,
});
expect(user1.purchased.plan.perkMonthCount).to.equal(0);
expect(user1.purchased.plan.consecutive.count).to.equal(2);
expect(user1.purchased.plan.consecutive.offset).to.equal(0);
expect(user1.purchased.plan.consecutive.trinkets).to.equal(1);
expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(5);
});
it('increments consecutive benefits after the third month', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(3, 'months')
.add(2, 'days')
@@ -315,6 +336,30 @@ describe('cron', async () => {
expect(user1.purchased.plan.consecutive.trinkets).to.equal(3);
expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(15);
});
it('initializes plan.perkMonthCount if necessary', async () => {
user.purchased.plan.perkMonthCount = undefined;
clock = sinon.useFakeTimers(moment(user.purchased.plan.dateUpdated)
.utcOffset(0)
.startOf('month')
.add(1, 'months')
.add(2, 'days')
.toDate());
await cron({
user, tasksByType, daysMissed, analytics,
});
expect(user.purchased.plan.perkMonthCount).to.equal(1);
user.purchased.plan.perkMonthCount = undefined;
user.purchased.plan.consecutive.count = 8;
clock.restore();
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(2, 'months')
.add(2, 'days')
.toDate());
await cron({
user, tasksByType, daysMissed, analytics,
});
expect(user.purchased.plan.perkMonthCount).to.equal(2);
});
});
describe('for a 3-month recurring subscription', async () => {
@@ -330,13 +375,16 @@ describe('cron', async () => {
},
});
// user3 has a 3-month recurring subscription starting today
user3.purchased.plan.customerId = 'subscribedId';
user3.purchased.plan.dateUpdated = moment().toDate();
user3.purchased.plan.planId = 'basic_3mo';
user3.purchased.plan.consecutive.count = 0;
user3.purchased.plan.consecutive.offset = 3;
user3.purchased.plan.consecutive.trinkets = 1;
user3.purchased.plan.consecutive.gemCapExtra = 5;
beforeEach(async () => {
user3.purchased.plan.customerId = 'subscribedId';
user3.purchased.plan.dateUpdated = moment().toDate();
user3.purchased.plan.planId = 'basic_3mo';
user3.purchased.plan.perkMonthCount = 0;
user3.purchased.plan.consecutive.count = 0;
user3.purchased.plan.consecutive.offset = 3;
user3.purchased.plan.consecutive.trinkets = 1;
user3.purchased.plan.consecutive.gemCapExtra = 5;
});
it('does not increment consecutive benefits in the first month of the first paid period that they already have benefits for', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(1, 'months')
@@ -390,6 +438,21 @@ describe('cron', async () => {
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(10);
});
it('keeps existing plan.perkMonthCount intact when incrementing consecutive benefits', async () => {
user3.purchased.plan.perkMonthCount = 2;
user3.purchased.plan.consecutive.trinkets = 1;
user3.purchased.plan.consecutive.gemCapExtra = 5;
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(4, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user3, tasksByType, daysMissed, analytics,
});
expect(user3.purchased.plan.perkMonthCount).to.equal(2);
expect(user3.purchased.plan.consecutive.trinkets).to.equal(2);
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(10);
});
it('does not increment consecutive benefits in the second month of the second period that they already have benefits for', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(5, 'months')
.add(2, 'days')
@@ -456,13 +519,16 @@ describe('cron', async () => {
},
});
// user6 has a 6-month recurring subscription starting today
user6.purchased.plan.customerId = 'subscribedId';
user6.purchased.plan.dateUpdated = moment().toDate();
user6.purchased.plan.planId = 'google_6mo';
user6.purchased.plan.consecutive.count = 0;
user6.purchased.plan.consecutive.offset = 6;
user6.purchased.plan.consecutive.trinkets = 2;
user6.purchased.plan.consecutive.gemCapExtra = 10;
beforeEach(async () => {
user6.purchased.plan.customerId = 'subscribedId';
user6.purchased.plan.dateUpdated = moment().toDate();
user6.purchased.plan.planId = 'google_6mo';
user6.purchased.plan.perkMonthCount = 0;
user6.purchased.plan.consecutive.count = 0;
user6.purchased.plan.consecutive.offset = 6;
user6.purchased.plan.consecutive.trinkets = 2;
user6.purchased.plan.consecutive.gemCapExtra = 10;
});
it('does not increment consecutive benefits in the first month of the first paid period that they already have benefits for', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(1, 'months')
@@ -503,6 +569,19 @@ describe('cron', async () => {
expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(20);
});
it('keeps existing plan.perkMonthCount intact when incrementing consecutive benefits', async () => {
user6.purchased.plan.perkMonthCount = 2;
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(7, 'months')
.add(2, 'days')
.toDate());
await cron({
user: user6, tasksByType, daysMissed, analytics,
});
expect(user6.purchased.plan.perkMonthCount).to.equal(2);
expect(user6.purchased.plan.consecutive.trinkets).to.equal(4);
expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(20);
});
it('increments consecutive benefits the month after the third paid period has started', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(13, 'months')
.add(2, 'days')
+296 -29
View File
@@ -29,8 +29,9 @@ describe('Apple Payments', () => {
.resolves();
iapValidateStub = sinon.stub(iap, 'validate')
.resolves({});
iapIsValidatedStub = sinon.stub(iap, 'isValidated')
.returns(true);
iapIsValidatedStub = sinon.stub(iap, 'isValidated').returns(true);
sinon.stub(iap, 'isExpired').returns(false);
sinon.stub(iap, 'isCanceled').returns(false);
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
.returns([{
productId: 'com.habitrpg.ios.Habitica.21gems',
@@ -44,6 +45,8 @@ describe('Apple Payments', () => {
iap.setup.restore();
iap.validate.restore();
iap.isValidated.restore();
iap.isExpired.restore();
iap.isCanceled.restore();
iap.getPurchaseData.restore();
payments.buySkuItem.restore();
gems.validateGiftMessage.restore();
@@ -218,6 +221,7 @@ describe('Apple Payments', () => {
headers = {};
receipt = `{"token": "${token}"}`;
nextPaymentProcessing = moment.utc().add({ days: 2 });
user = new User();
iapSetupStub = sinon.stub(iap, 'setup')
.resolves();
@@ -228,14 +232,17 @@ describe('Apple Payments', () => {
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
.returns([{
expirationDate: moment.utc().subtract({ day: 1 }).toDate(),
purchaseDate: moment.utc().valueOf(),
productId: sku,
transactionId: token,
}, {
expirationDate: moment.utc().add({ day: 1 }).toDate(),
purchaseDate: moment.utc().valueOf(),
productId: 'wrongsku',
transactionId: token,
}, {
expirationDate: moment.utc().add({ day: 1 }).toDate(),
purchaseDate: moment.utc().valueOf(),
productId: sku,
transactionId: token,
}]);
@@ -250,21 +257,12 @@ describe('Apple Payments', () => {
if (payments.createSubscription.restore) payments.createSubscription.restore();
});
it('should throw an error if sku is empty', async () => {
await expect(applePayments.subscribe('', user, receipt, headers, nextPaymentProcessing))
.to.eventually.be.rejected.and.to.eql({
httpCode: 400,
name: 'BadRequest',
message: i18n.t('missingSubscriptionCode'),
});
});
it('should throw an error if receipt is invalid', async () => {
iap.isValidated.restore();
iapIsValidatedStub = sinon.stub(iap, 'isValidated')
.returns(false);
await expect(applePayments.subscribe(sku, user, receipt, headers, nextPaymentProcessing))
await expect(applePayments.subscribe(user, receipt, headers, nextPaymentProcessing))
.to.eventually.be.rejected.and.to.eql({
httpCode: 401,
name: 'NotAuthorized',
@@ -295,13 +293,15 @@ describe('Apple Payments', () => {
iap.getPurchaseData.restore();
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
.returns([{
expirationDate: moment.utc().add({ day: 1 }).toDate(),
expirationDate: moment.utc().add({ day: 2 }).toDate(),
purchaseDate: new Date(),
productId: option.sku,
transactionId: token,
originalTransactionId: token,
}]);
sub = common.content.subscriptionBlocks[option.subKey];
await applePayments.subscribe(option.sku, user, receipt, headers, nextPaymentProcessing);
await applePayments.subscribe(user, receipt, headers, nextPaymentProcessing);
expect(iapSetupStub).to.be.calledOnce;
expect(iapValidateStub).to.be.calledOnce;
@@ -321,21 +321,253 @@ describe('Apple Payments', () => {
nextPaymentProcessing,
});
});
if (option !== subOptions[3]) {
const newOption = subOptions[3];
it(`upgrades a subscription from ${option.sku} to ${newOption.sku}`, async () => {
const oldSub = common.content.subscriptionBlocks[option.subKey];
oldSub.logic = 'refundAndRepay';
user.profile.name = 'sender';
user.purchased.plan.paymentMethod = applePayments.constants.PAYMENT_METHOD_APPLE;
user.purchased.plan.customerId = token;
user.purchased.plan.planId = option.subKey;
user.purchased.plan.additionalData = receipt;
iap.getPurchaseData.restore();
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
.returns([{
expirationDate: moment.utc().add({ day: 2 }).toDate(),
purchaseDate: moment.utc().valueOf(),
productId: newOption.sku,
transactionId: `${token}new`,
originalTransactionId: token,
}]);
sub = common.content.subscriptionBlocks[newOption.subKey];
await applePayments.subscribe(user,
receipt,
headers,
nextPaymentProcessing);
expect(iapSetupStub).to.be.calledOnce;
expect(iapValidateStub).to.be.calledOnce;
expect(iapValidateStub).to.be.calledWith(iap.APPLE, receipt);
expect(iapIsValidatedStub).to.be.calledOnce;
expect(iapIsValidatedStub).to.be.calledWith({});
expect(iapGetPurchaseDataStub).to.be.calledOnce;
expect(paymentsCreateSubscritionStub).to.be.calledOnce;
expect(paymentsCreateSubscritionStub).to.be.calledWith({
user,
customerId: token,
paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
sub,
headers,
additionalData: receipt,
nextPaymentProcessing,
updatedFrom: oldSub,
});
});
}
if (option !== subOptions[0]) {
const newOption = subOptions[0];
it(`downgrades a subscription from ${option.sku} to ${newOption.sku}`, async () => {
const oldSub = common.content.subscriptionBlocks[option.subKey];
user.profile.name = 'sender';
user.purchased.plan.paymentMethod = applePayments.constants.PAYMENT_METHOD_APPLE;
user.purchased.plan.customerId = token;
user.purchased.plan.planId = option.subKey;
user.purchased.plan.additionalData = receipt;
iap.getPurchaseData.restore();
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
.returns([{
expirationDate: moment.utc().add({ day: 2 }).toDate(),
purchaseDate: moment.utc().valueOf(),
productId: newOption.sku,
transactionId: `${token}new`,
originalTransactionId: token,
}]);
sub = common.content.subscriptionBlocks[newOption.subKey];
await applePayments.subscribe(user,
receipt,
headers,
nextPaymentProcessing);
expect(iapSetupStub).to.be.calledOnce;
expect(iapValidateStub).to.be.calledOnce;
expect(iapValidateStub).to.be.calledWith(iap.APPLE, receipt);
expect(iapIsValidatedStub).to.be.calledOnce;
expect(iapIsValidatedStub).to.be.calledWith({});
expect(iapGetPurchaseDataStub).to.be.calledOnce;
expect(paymentsCreateSubscritionStub).to.be.calledOnce;
expect(paymentsCreateSubscritionStub).to.be.calledWith({
user,
customerId: token,
paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
sub,
headers,
additionalData: receipt,
nextPaymentProcessing,
updatedFrom: oldSub,
});
});
}
});
it('errors when a user is already subscribed', async () => {
payments.createSubscription.restore();
user = new User();
await user.save();
it('uses the most recent subscription data', async () => {
iap.getPurchaseData.restore();
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
.returns([{
expirationDate: moment.utc().add({ day: 4 }).toDate(),
purchaseDate: moment.utc().subtract({ day: 5 }).toDate(),
productId: 'com.habitrpg.ios.habitica.subscription.3month',
transactionId: `${token}oldest`,
originalTransactionId: `${token}evenOlder`,
}, {
expirationDate: moment.utc().add({ day: 2 }).toDate(),
purchaseDate: moment.utc().subtract({ day: 1 }).toDate(),
productId: 'com.habitrpg.ios.habitica.subscription.12month',
transactionId: `${token}newest`,
originalTransactionId: `${token}newest`,
}, {
expirationDate: moment.utc().add({ day: 1 }).toDate(),
purchaseDate: moment.utc().subtract({ day: 2 }).toDate(),
productId: 'com.habitrpg.ios.habitica.subscription.6month',
transactionId: token,
originalTransactionId: token,
}]);
sub = common.content.subscriptionBlocks.basic_12mo;
await applePayments.subscribe(sku, user, receipt, headers, nextPaymentProcessing);
await applePayments.subscribe(user, receipt, headers, nextPaymentProcessing);
await expect(applePayments.subscribe(sku, user, receipt, headers, nextPaymentProcessing))
.to.eventually.be.rejected.and.to.eql({
httpCode: 401,
name: 'NotAuthorized',
message: applePayments.constants.RESPONSE_ALREADY_USED,
});
expect(paymentsCreateSubscritionStub).to.be.calledOnce;
expect(paymentsCreateSubscritionStub).to.be.calledWith({
user,
customerId: `${token}newest`,
paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
sub,
headers,
additionalData: receipt,
nextPaymentProcessing,
});
});
describe('does not apply multiple times', async () => {
it('errors when a user is using the same subscription', async () => {
payments.createSubscription.restore();
iap.getPurchaseData.restore();
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
.returns([{
expirationDate: moment.utc().add({ day: 1 }).toDate(),
purchaseDate: moment.utc().toDate(),
productId: sku,
transactionId: token,
originalTransactionId: token,
}]);
await applePayments.subscribe(user, receipt, headers, nextPaymentProcessing);
await expect(applePayments.subscribe(user, receipt, headers, nextPaymentProcessing))
.to.eventually.be.rejected.and.to.eql({
httpCode: 401,
name: 'NotAuthorized',
message: applePayments.constants.RESPONSE_ALREADY_USED,
});
});
it('errors when a user is using a rebill of the same subscription', async () => {
user = new User();
await user.save();
payments.createSubscription.restore();
iap.getPurchaseData.restore();
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
.returns([{
expirationDate: moment.utc().add({ day: 1 }).toDate(),
purchaseDate: moment.utc().toDate(),
productId: sku,
transactionId: `${token}renew`,
originalTransactionId: token,
}]);
await applePayments.subscribe(user, receipt, headers, nextPaymentProcessing);
await expect(applePayments.subscribe(user, receipt, headers, nextPaymentProcessing))
.to.eventually.be.rejected.and.to.eql({
httpCode: 401,
name: 'NotAuthorized',
message: applePayments.constants.RESPONSE_ALREADY_USED,
});
});
it('errors when a different user is using the subscription', async () => {
user = new User();
await user.save();
payments.createSubscription.restore();
iap.getPurchaseData.restore();
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
.returns([{
expirationDate: moment.utc().add({ day: 1 }).toDate(),
purchaseDate: moment.utc().toDate(),
productId: sku,
transactionId: token,
originalTransactionId: token,
}]);
await applePayments.subscribe(user, receipt, headers, nextPaymentProcessing);
const secondUser = new User();
await secondUser.save();
await expect(applePayments.subscribe(
secondUser, receipt, headers, nextPaymentProcessing,
))
.to.eventually.be.rejected.and.to.eql({
httpCode: 401,
name: 'NotAuthorized',
message: applePayments.constants.RESPONSE_ALREADY_USED,
});
});
it('errors when a multiple users exist using the subscription', async () => {
user = new User();
await user.save();
payments.createSubscription.restore();
iap.getPurchaseData.restore();
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
.returns([{
expirationDate: moment.utc().add({ day: 1 }).toDate(),
purchaseDate: moment.utc().toDate(),
productId: sku,
transactionId: token,
originalTransactionId: token,
}]);
await applePayments.subscribe(user, receipt, headers, nextPaymentProcessing);
const secondUser = new User();
secondUser.purchased.plan = user.purchased.plan;
secondUser.purchased.plan.dateTerminate = new Date();
secondUser.save();
iap.getPurchaseData.restore();
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
.returns([{
expirationDate: moment.utc().add({ day: 1 }).toDate(),
purchaseDate: moment.utc().toDate(),
productId: sku,
transactionId: `${token}new`,
originalTransactionId: token,
}]);
const thirdUser = new User();
await thirdUser.save();
await expect(applePayments.subscribe(
thirdUser, receipt, headers, nextPaymentProcessing,
))
.to.eventually.be.rejected.and.to.eql({
httpCode: 401,
name: 'NotAuthorized',
message: applePayments.constants.RESPONSE_ALREADY_USED,
});
});
});
});
@@ -360,9 +592,9 @@ describe('Apple Payments', () => {
});
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
.returns([{ expirationDate: expirationDate.toDate() }]);
iapIsValidatedStub = sinon.stub(iap, 'isValidated')
.returns(true);
iapIsValidatedStub = sinon.stub(iap, 'isValidated').returns(true);
sinon.stub(iap, 'isCanceled').returns(false);
sinon.stub(iap, 'isExpired').returns(true);
user = new User();
user.profile.name = 'sender';
user.purchased.plan.paymentMethod = applePayments.constants.PAYMENT_METHOD_APPLE;
@@ -377,6 +609,8 @@ describe('Apple Payments', () => {
iap.setup.restore();
iap.validate.restore();
iap.isValidated.restore();
iap.isExpired.restore();
iap.isCanceled.restore();
iap.getPurchaseData.restore();
payments.cancelSubscription.restore();
});
@@ -396,6 +630,8 @@ describe('Apple Payments', () => {
iap.getPurchaseData.restore();
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
.returns([{ expirationDate: expirationDate.add({ day: 1 }).toDate() }]);
iap.isExpired.restore();
sinon.stub(iap, 'isExpired').returns(false);
await expect(applePayments.cancelSubscribe(user, headers))
.to.eventually.be.rejected.and.to.eql({
@@ -418,7 +654,38 @@ describe('Apple Payments', () => {
});
});
it('should cancel a user subscription', async () => {
it('should cancel a cancelled subscription with termination date in the future', async () => {
const futureDate = expirationDate.add({ day: 1 });
iap.getPurchaseData.restore();
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
.returns([{ expirationDate: futureDate }]);
iap.isExpired.restore();
sinon.stub(iap, 'isExpired').returns(false);
iap.isCanceled.restore();
sinon.stub(iap, 'isCanceled').returns(true);
await applePayments.cancelSubscribe(user, headers);
expect(iapSetupStub).to.be.calledOnce;
expect(iapValidateStub).to.be.calledOnce;
expect(iapValidateStub).to.be.calledWith(iap.APPLE, receipt);
expect(iapIsValidatedStub).to.be.calledOnce;
expect(iapIsValidatedStub).to.be.calledWith({
expirationDate: futureDate,
});
expect(iapGetPurchaseDataStub).to.be.calledOnce;
expect(paymentCancelSubscriptionSpy).to.be.calledOnce;
expect(paymentCancelSubscriptionSpy).to.be.calledWith({
user,
paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
nextBill: futureDate.toDate(),
headers,
});
});
it('should cancel an expired subscription', async () => {
await applePayments.cancelSubscribe(user, headers);
expect(iapSetupStub).to.be.calledOnce;
+799 -2
View File
@@ -203,6 +203,28 @@ describe('payments/index', () => {
expect(recipient.purchased.plan.dateCreated).to.exist;
});
it('sets plan.dateCurrentTypeCreated if it did not previously exist', async () => {
expect(recipient.purchased.plan.dateCurrentTypeCreated).to.not.exist;
await api.createSubscription(data);
expect(recipient.purchased.plan.dateCurrentTypeCreated).to.exist;
});
it('keeps plan.dateCreated when changing subscription type', async () => {
await api.createSubscription(data);
const initialDate = recipient.purchased.plan.dateCreated;
await api.createSubscription(data);
expect(recipient.purchased.plan.dateCreated).to.eql(initialDate);
});
it('sets plan.dateCurrentTypeCreated when changing subscription type', async () => {
await api.createSubscription(data);
const initialDate = recipient.purchased.plan.dateCurrentTypeCreated;
await api.createSubscription(data);
expect(recipient.purchased.plan.dateCurrentTypeCreated).to.not.eql(initialDate);
});
it('does not change plan.customerId if it already exists', async () => {
recipient.purchased.plan = plan;
data.customerId = 'purchaserCustomerId';
@@ -213,6 +235,116 @@ describe('payments/index', () => {
expect(recipient.purchased.plan.customerId).to.eql('customer-id');
});
it('sets plan.perkMonthCount to 1 if user is not subscribed', async () => {
recipient.purchased.plan = plan;
recipient.purchased.plan.perkMonthCount = 1;
recipient.purchased.plan.customerId = undefined;
data.sub.key = 'basic_earned';
data.gift.subscription.key = 'basic_earned';
data.gift.subscription.months = 1;
expect(recipient.purchased.plan.perkMonthCount).to.eql(1);
await api.createSubscription(data);
expect(recipient.purchased.plan.perkMonthCount).to.eql(1);
});
it('sets plan.perkMonthCount to 1 if field is not initialized', async () => {
recipient.purchased.plan = plan;
recipient.purchased.plan.perkMonthCount = -1;
recipient.purchased.plan.customerId = undefined;
data.sub.key = 'basic_earned';
data.gift.subscription.key = 'basic_earned';
data.gift.subscription.months = 1;
expect(recipient.purchased.plan.perkMonthCount).to.eql(-1);
await api.createSubscription(data);
expect(recipient.purchased.plan.perkMonthCount).to.eql(1);
});
it('sets plan.perkMonthCount to 1 if user had previous count but lapsed subscription', async () => {
recipient.purchased.plan = plan;
recipient.purchased.plan.perkMonthCount = 2;
recipient.purchased.plan.customerId = undefined;
data.sub.key = 'basic_earned';
data.gift.subscription.key = 'basic_earned';
data.gift.subscription.months = 1;
expect(recipient.purchased.plan.perkMonthCount).to.eql(2);
await api.createSubscription(data);
expect(recipient.purchased.plan.perkMonthCount).to.eql(1);
});
it('adds to plan.perkMonthCount if user is already subscribed', async () => {
recipient.purchased.plan = plan;
recipient.purchased.plan.perkMonthCount = 1;
data.sub.key = 'basic_earned';
data.gift.subscription.key = 'basic_earned';
data.gift.subscription.months = 1;
expect(recipient.purchased.plan.perkMonthCount).to.eql(1);
await api.createSubscription(data);
expect(recipient.purchased.plan.perkMonthCount).to.eql(2);
});
it('awards perks if plan.perkMonthCount reaches 3 with existing subscription', async () => {
recipient.purchased.plan = plan;
recipient.purchased.plan.perkMonthCount = 2;
data.sub.key = 'basic_earned';
data.gift.subscription.key = 'basic_earned';
data.gift.subscription.months = 1;
expect(recipient.purchased.plan.perkMonthCount).to.eql(2);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0);
await api.createSubscription(data);
expect(recipient.purchased.plan.perkMonthCount).to.eql(0);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(5);
});
it('awards perks if plan.perkMonthCount reaches 3 without existing subscription', async () => {
recipient.purchased.plan.perkMonthCount = 0;
expect(recipient.purchased.plan.perkMonthCount).to.eql(0);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0);
await api.createSubscription(data);
expect(recipient.purchased.plan.perkMonthCount).to.eql(0);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(5);
});
it('awards perks if plan.perkMonthCount reaches 3 without initialized field', async () => {
expect(recipient.purchased.plan.perkMonthCount).to.eql(-1);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0);
await api.createSubscription(data);
expect(recipient.purchased.plan.perkMonthCount).to.eql(0);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(5);
});
it('awards perks if plan.perkMonthCount goes over 3', async () => {
recipient.purchased.plan = plan;
recipient.purchased.plan.perkMonthCount = 2;
data.sub.key = 'basic_earned';
expect(recipient.purchased.plan.perkMonthCount).to.eql(2);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0);
await api.createSubscription(data);
expect(recipient.purchased.plan.perkMonthCount).to.eql(2);
expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1);
expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(5);
});
it('sets plan.customerId to "Gift" if it does not already exist', async () => {
expect(recipient.purchased.plan.customerId).to.not.exist;
@@ -379,6 +511,7 @@ describe('payments/index', () => {
expect(user.purchased.plan.customerId).to.eql('customer-id');
expect(user.purchased.plan.dateUpdated).to.exist;
expect(user.purchased.plan.gemsBought).to.eql(0);
expect(user.purchased.plan.perkMonthCount).to.eql(0);
expect(user.purchased.plan.paymentMethod).to.eql('Payment Method');
expect(user.purchased.plan.extraMonths).to.eql(0);
expect(user.purchased.plan.dateTerminated).to.eql(null);
@@ -386,6 +519,63 @@ describe('payments/index', () => {
expect(user.purchased.plan.dateCreated).to.exist;
});
it('sets plan.dateCreated if it did not previously exist', async () => {
expect(user.purchased.plan.dateCreated).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.dateCreated).to.exist;
});
it('sets plan.dateCurrentTypeCreated if it did not previously exist', async () => {
expect(user.purchased.plan.dateCurrentTypeCreated).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.dateCurrentTypeCreated).to.exist;
});
it('keeps plan.dateCreated when changing subscription type', async () => {
await api.createSubscription(data);
const initialDate = user.purchased.plan.dateCreated;
await api.createSubscription(data);
expect(user.purchased.plan.dateCreated).to.eql(initialDate);
});
it('sets plan.dateCurrentTypeCreated when changing subscription type', async () => {
await api.createSubscription(data);
const initialDate = user.purchased.plan.dateCurrentTypeCreated;
await api.createSubscription(data);
expect(user.purchased.plan.dateCurrentTypeCreated).to.not.eql(initialDate);
});
it('keeps plan.perkMonthCount when changing subscription type', async () => {
await api.createSubscription(data);
user.purchased.plan.perkMonthCount = 2;
await api.createSubscription(data);
expect(user.purchased.plan.perkMonthCount).to.eql(2);
});
it('sets plan.perkMonthCount to zero when creating new monthly subscription', async () => {
user.purchased.plan.perkMonthCount = 2;
await api.createSubscription(data);
expect(user.purchased.plan.perkMonthCount).to.eql(0);
});
it('sets plan.perkMonthCount to zero when creating new 3 month subscription', async () => {
user.purchased.plan.perkMonthCount = 2;
await api.createSubscription(data);
expect(user.purchased.plan.perkMonthCount).to.eql(0);
});
it('updates plan.consecutive.offset when changing subscription type', async () => {
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.offset).to.eql(3);
data.sub.key = 'basic_6mo';
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.offset).to.eql(6);
});
it('awards the Royal Purple Jackalope pet', async () => {
await api.createSubscription(data);
@@ -465,6 +655,89 @@ describe('payments/index', () => {
},
});
});
context('Upgrades subscription', () => {
it('from basic_earned to basic_6mo', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.customerId).to.eql('customer-id');
const created = user.purchased.plan.dateCreated;
const updated = user.purchased.plan.dateUpdated;
data.sub.key = 'basic_6mo';
data.updatedFrom = { key: 'basic_earned' };
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.dateCreated).to.eql(created);
expect(user.purchased.plan.dateUpdated).to.not.eql(updated);
expect(user.purchased.plan.customerId).to.eql('customer-id');
});
it('from basic_3mo to basic_12mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_3mo');
expect(user.purchased.plan.customerId).to.eql('customer-id');
const created = user.purchased.plan.dateCreated;
const updated = user.purchased.plan.dateUpdated;
data.sub.key = 'basic_12mo';
data.updatedFrom = { key: 'basic_3mo' };
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.dateCreated).to.eql(created);
expect(user.purchased.plan.dateUpdated).to.not.eql(updated);
expect(user.purchased.plan.customerId).to.eql('customer-id');
});
});
context('Downgrades subscription', () => {
it('from basic_6mo to basic_earned', async () => {
data.sub.key = 'basic_6mo';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.customerId).to.eql('customer-id');
const created = user.purchased.plan.dateCreated;
const updated = user.purchased.plan.dateUpdated;
data.sub.key = 'basic_earned';
data.updatedFrom = { key: 'basic_6mo' };
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.dateCreated).to.eql(created);
expect(user.purchased.plan.dateUpdated).to.not.eql(updated);
expect(user.purchased.plan.customerId).to.eql('customer-id');
});
it('from basic_12mo to basic_3mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
data.sub.key = 'basic_12mo';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.customerId).to.eql('customer-id');
const created = user.purchased.plan.dateCreated;
const updated = user.purchased.plan.dateUpdated;
data.sub.key = 'basic_3mo';
data.updatedFrom = { key: 'basic_12mo' };
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_3mo');
expect(user.purchased.plan.dateCreated).to.eql(created);
expect(user.purchased.plan.dateUpdated).to.not.eql(updated);
expect(user.purchased.plan.customerId).to.eql('customer-id');
});
});
});
context('Block subscription perks', () => {
@@ -488,7 +761,6 @@ describe('payments/index', () => {
it('adds 10 to plan.consecutive.gemCapExtra for 6 month block', async () => {
data.sub.key = 'basic_6mo';
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
@@ -496,7 +768,6 @@ describe('payments/index', () => {
it('adds 20 to plan.consecutive.gemCapExtra for 12 month block', async () => {
data.sub.key = 'basic_12mo';
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20);
@@ -532,6 +803,532 @@ describe('payments/index', () => {
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
});
context('Upgrades subscription', () => {
context('Using payDifference logic', () => {
beforeEach(async () => {
data.updatedFrom = { logic: 'payDifference' };
});
it('Adds 10 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
data.sub.key = 'basic_6mo';
data.updatedFrom.key = 'basic_earned';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
});
it('Adds 15 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_3mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_3mo';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20);
});
it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
data.sub.key = 'basic_6mo';
data.updatedFrom.key = 'basic_earned';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
});
it('Adds 2 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => {
data.sub.key = 'basic_6mo';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_6mo';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
});
it('Adds 3 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_3mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_3mo';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
});
});
context('Using payFull logic', () => {
beforeEach(async () => {
data.updatedFrom = { logic: 'payFull' };
});
it('Adds 10 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
data.sub.key = 'basic_6mo';
data.updatedFrom.key = 'basic_earned';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
});
it('Adds 20 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_3mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_3mo';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(25);
});
it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
data.sub.key = 'basic_6mo';
data.updatedFrom.key = 'basic_earned';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
});
it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => {
data.sub.key = 'basic_6mo';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_6mo';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(6);
});
it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_3mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_3mo';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(5);
});
});
context('Using refundAndRepay logic', () => {
let clock;
beforeEach(async () => {
clock = sinon.useFakeTimers(new Date('2022-01-01'));
data.updatedFrom = { logic: 'refundAndRepay' };
});
context('Upgrades within first half of subscription', () => {
it('Adds 10 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
data.sub.key = 'basic_6mo';
data.updatedFrom.key = 'basic_earned';
clock.restore();
clock = sinon.useFakeTimers(new Date('2022-01-10'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
});
it('Adds 15 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_3mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_3mo';
clock.restore();
clock = sinon.useFakeTimers(new Date('2022-02-05'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20);
});
it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
data.sub.key = 'basic_6mo';
data.updatedFrom.key = 'basic_earned';
clock.restore();
clock = sinon.useFakeTimers(new Date('2022-01-08'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
});
it('Adds 3 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_3mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_3mo';
clock.restore();
clock = sinon.useFakeTimers(new Date('2022-01-31'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
});
it('Adds 2 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => {
data.sub.key = 'basic_6mo';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_6mo';
clock.restore();
clock = sinon.useFakeTimers(new Date('2022-01-28'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
});
it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo after initial cycle', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
data.sub.key = 'basic_6mo';
data.updatedFrom.key = 'basic_earned';
clock.restore();
clock = sinon.useFakeTimers(new Date('2024-01-08'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
});
it('Adds 2 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo after initial cycle', async () => {
data.sub.key = 'basic_6mo';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_6mo';
clock.restore();
clock = sinon.useFakeTimers(new Date('2022-08-28'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
});
it('Adds 3 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo after initial cycle', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_3mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_3mo';
clock.restore();
clock = sinon.useFakeTimers(new Date('2022-07-31'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
});
});
context('Upgrades within second half of subscription', () => {
it('Adds 10 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
data.sub.key = 'basic_6mo';
data.updatedFrom.key = 'basic_earned';
clock.restore();
clock = sinon.useFakeTimers(new Date('2022-01-20'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
});
it('Adds 20 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_3mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_3mo';
clock.restore();
clock = sinon.useFakeTimers(new Date('2022-02-24'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(25);
});
it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
data.sub.key = 'basic_6mo';
data.updatedFrom.key = 'basic_earned';
clock.restore();
clock = sinon.useFakeTimers(new Date('2022-01-28'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
});
it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => {
data.sub.key = 'basic_6mo';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_6mo';
clock.restore();
clock = sinon.useFakeTimers(new Date('2022-05-28'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(6);
});
it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_3mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_3mo';
clock.restore();
clock = sinon.useFakeTimers(new Date('2022-03-03'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(5);
});
it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo after initial cycle', async () => {
data.sub.key = 'basic_earned';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
data.sub.key = 'basic_6mo';
data.updatedFrom.key = 'basic_earned';
clock.restore();
clock = sinon.useFakeTimers(new Date('2022-05-28'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
});
it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo after initial cycle', async () => {
data.sub.key = 'basic_6mo';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_6mo';
clock.restore();
clock = sinon.useFakeTimers(new Date('2023-05-28'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(6);
});
it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo after initial cycle', async () => {
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_3mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
data.sub.key = 'basic_12mo';
data.updatedFrom.key = 'basic_3mo';
clock.restore();
clock = sinon.useFakeTimers(new Date('2023-09-03'));
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(5);
});
});
afterEach(async () => {
if (clock !== null) clock.restore();
});
});
});
context('Downgrades subscription', () => {
it('does not remove from plan.consecutive.gemCapExtra from basic_6mo to basic_earned', async () => {
data.sub.key = 'basic_6mo';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
data.sub.key = 'basic_earned';
data.updatedFrom = { key: 'basic_6mo' };
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
});
it('does not remove from plan.consecutive.gemCapExtra from basic_12mo to basic_3mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
data.sub.key = 'basic_12mo';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20);
data.sub.key = 'basic_3mo';
data.updatedFrom = { key: 'basic_12mo' };
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20);
});
it('does not remove from plan.consecutive.trinkets from basic_6mo to basic_earned', async () => {
data.sub.key = 'basic_6mo';
expect(user.purchased.plan.planId).to.not.exist;
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_6mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
data.sub.key = 'basic_earned';
data.updatedFrom = { key: 'basic_6mo' };
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_earned');
expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
});
it('does not remove from plan.consecutive.trinkets from basic_12mo to basic_3mo', async () => {
expect(user.purchased.plan.planId).to.not.exist;
data.sub.key = 'basic_12mo';
await api.createSubscription(data);
expect(user.purchased.plan.planId).to.eql('basic_12mo');
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
data.sub.key = 'basic_3mo';
data.updatedFrom = { key: 'basic_12mo' };
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
});
});
});
context('Mystery Items', () => {
@@ -2,7 +2,6 @@ import { v4 as generateUUID } from 'uuid';
import {
generateUser,
createAndPopulateGroup,
checkExistence,
translate as t,
} from '../../../../helpers/api-integration/v3';
@@ -258,47 +257,6 @@ describe('POST /group/:groupId/join', () => {
await expect(user.get('/user')).to.eventually.have.nested.property('items.quests.basilist', 2);
});
it('deletes previous party where the user was the only member', async () => {
const userToInvite = await generateUser();
const oldParty = await userToInvite.post('/groups', { // add user to a party
name: 'Another Test Party',
type: 'party',
});
await expect(checkExistence('groups', oldParty._id)).to.eventually.equal(true);
await user.post(`/groups/${party._id}/invite`, {
uuids: [userToInvite._id],
});
await userToInvite.post(`/groups/${party._id}/join`);
await expect(user.get('/user')).to.eventually.have.nested.property('party._id', party._id);
await expect(checkExistence('groups', oldParty._id)).to.eventually.equal(false);
});
it('does not allow user to leave a party if a quest was active and they were the only member', async () => {
const userToInvite = await generateUser();
const oldParty = await userToInvite.post('/groups', { // add user to a party
name: 'Another Test Party',
type: 'party',
});
await userToInvite.update({
[`items.quests.${PET_QUEST}`]: 1,
});
await userToInvite.post(`/groups/${oldParty._id}/quests/invite/${PET_QUEST}`);
await expect(checkExistence('groups', oldParty._id)).to.eventually.equal(true);
await user.post(`/groups/${party._id}/invite`, {
uuids: [userToInvite._id],
});
await expect(userToInvite.post(`/groups/${party._id}/join`)).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('messageCannotLeaveWhileQuesting'),
});
});
it('invites joining member to active quest', async () => {
await user.update({
[`items.quests.${PET_QUEST}`]: 1,
@@ -1,6 +1,7 @@
import { v4 as generateUUID } from 'uuid';
import nconf from 'nconf';
import {
createAndPopulateGroup,
generateUser,
generateGroup,
translate as t,
@@ -581,20 +582,7 @@ describe('Post /groups/:groupId/invite', () => {
});
});
it('allow inviting a user to a party if they are partying solo', async () => {
const userToInvite = await generateUser();
await userToInvite.post('/groups', { // add user to a party
name: 'Another Test Party',
type: 'party',
});
await inviter.post(`/groups/${party._id}/invite`, {
uuids: [userToInvite._id],
});
expect((await userToInvite.get('/user')).invitations.parties[0].id).to.equal(party._id);
});
it('allow inviting a user to 2 different parties', async () => {
it('allows inviting a user to 2 different parties', async () => {
// Create another inviter
const inviter2 = await generateUser();
@@ -635,29 +623,48 @@ describe('Post /groups/:groupId/invite', () => {
});
expect((await userToInvite.get('/user')).invitations.parties[0].id).to.equal(party._id);
});
});
describe('party size limits', () => {
let party;
let partyLeader;
beforeEach(async () => {
group = await createAndPopulateGroup({
groupDetails: {
name: 'Test Party',
type: 'party',
privacy: 'private',
},
// Generate party with 20 members
members: PARTY_LIMIT_MEMBERS - 10,
});
party = group.group;
partyLeader = group.groupLeader;
});
it('allows 30 members in a party', async () => {
const invitesToGenerate = [];
// Generate 29 users to invite (29 + leader = 30 members)
for (let i = 0; i < PARTY_LIMIT_MEMBERS - 1; i += 1) {
// Generate 10 new invites
for (let i = 1; i < 10; i += 1) {
invitesToGenerate.push(generateUser());
}
const generatedInvites = await Promise.all(invitesToGenerate);
// Invite users
expect(await inviter.post(`/groups/${party._id}/invite`, {
expect(await partyLeader.post(`/groups/${party._id}/invite`, {
uuids: generatedInvites.map(invite => invite._id),
})).to.be.an('array');
}).timeout(10000);
it('does not allow 30+ members in a party', async () => {
it('does not allow >30 members in a party', async () => {
const invitesToGenerate = [];
// Generate 30 users to invite (30 + leader = 31 members)
for (let i = 0; i < PARTY_LIMIT_MEMBERS; i += 1) {
// Generate 11 invites
for (let i = 1; i < 11; i += 1) {
invitesToGenerate.push(generateUser());
}
const generatedInvites = await Promise.all(invitesToGenerate);
// Invite users
await expect(inviter.post(`/groups/${party._id}/invite`, {
await expect(partyLeader.post(`/groups/${party._id}/invite`, {
uuids: generatedInvites.map(invite => invite._id),
}))
.to.eventually.be.rejected.and.eql({
@@ -45,11 +45,10 @@ describe('payments : apple #subscribe', () => {
});
expect(subscribeStub).to.be.calledOnce;
expect(subscribeStub.args[0][0]).to.eql(sku);
expect(subscribeStub.args[0][1]._id).to.eql(user._id);
expect(subscribeStub.args[0][2]).to.eql('receipt');
expect(subscribeStub.args[0][3]['x-api-key']).to.eql(user.apiToken);
expect(subscribeStub.args[0][3]['x-api-user']).to.eql(user._id);
expect(subscribeStub.args[0][0]._id).to.eql(user._id);
expect(subscribeStub.args[0][1]).to.eql('receipt');
expect(subscribeStub.args[0][2]['x-api-key']).to.eql(user.apiToken);
expect(subscribeStub.args[0][2]['x-api-user']).to.eql(user._id);
});
});
});
+12
View File
@@ -215,6 +215,7 @@ describe('cron utility functions', () => {
it('monthly plan, next date in 3 months', () => {
const user = baseUserData(60, 0, 'group_plan_auto');
user.purchased.plan.perkMonthCount = 0;
const planContext = getPlanContext(user, now);
@@ -224,6 +225,7 @@ describe('cron utility functions', () => {
it('monthly plan, next date in 1 month', () => {
const user = baseUserData(62, 0, 'group_plan_auto');
user.purchased.plan.perkMonthCount = 2;
const planContext = getPlanContext(user, now);
@@ -248,5 +250,15 @@ describe('cron utility functions', () => {
expect(planContext.nextHourglassDate)
.to.be.sameMoment('2022-07-10T02:00:00.144Z');
});
it('multi-month plan with perk count', () => {
const user = baseUserData(60, 1, 'basic_3mo');
user.purchased.plan.perkMonthCount = 2;
const planContext = getPlanContext(user, now);
expect(planContext.nextHourglassDate)
.to.be.sameMoment('2022-07-10T02:00:00.144Z');
});
});
});
@@ -12,8 +12,9 @@ const webhookData = {};
app.use(bodyParser.urlencoded({
extended: true,
limit: '10mb',
}));
app.use(bodyParser.json());
app.use(bodyParser.json({ limit: '10mb' }));
app.post('/webhooks/:id', (req, res) => {
const { id } = req.params;
+2 -1
View File
@@ -53,7 +53,8 @@ function _requestMaker (user, method, additionalSets = {}) {
if (user && user._id && user.apiToken) {
request
.set('x-api-user', user._id)
.set('x-api-key', user.apiToken);
.set('x-api-key', user.apiToken)
.set('x-client', 'habitica-web');
}
if (!isEmpty(additionalSets)) {
+55 -70
View File
@@ -13318,11 +13318,34 @@
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"emojis-list": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"optional": true
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"loader-utils": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
@@ -13354,6 +13377,38 @@
"ansi-regex": "^5.0.1"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.8.3",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.3.tgz",
"integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==",
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
}
}
},
"wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
@@ -30574,76 +30629,6 @@
}
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.8.3",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.3.tgz",
"integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==",
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"emojis-list": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q=="
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
},
"loader-utils": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"vue-mugen-scroll": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/vue-mugen-scroll/-/vue-mugen-scroll-0.2.6.tgz",
+3
View File
@@ -35,6 +35,7 @@
<sub-canceled-modal v-if="isUserLoaded" />
<bug-report-modal v-if="isUserLoaded" />
<bug-report-success-modal v-if="isUserLoaded" />
<external-link-modal />
<birthday-modal />
<snackbars />
<router-view v-if="!isUserLoggedIn || isStaticPage" />
@@ -175,6 +176,7 @@ import amazonPaymentsModal from '@/components/payments/amazonModal';
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 spellsMixin from '@/mixins/spells';
import {
@@ -210,6 +212,7 @@ export default {
subCanceledModal,
bugReportModal,
bugReportSuccessModal,
externalLinkModal,
},
mixins: [notifications, spellsMixin],
data () {
@@ -820,6 +820,11 @@
width: 141px;
height: 147px;
}
.background_cretaceous_forest {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_cretaceous_forest.png');
width: 141px;
height: 147px;
}
.background_crosscountry_ski_trail {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_crosscountry_ski_trail.png');
width: 141px;
@@ -1054,6 +1059,11 @@
width: 141px;
height: 147px;
}
.background_flying_over_hedge_maze {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_flying_over_hedge_maze.png');
width: 141px;
height: 147px;
}
.background_flying_over_icy_steppes {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_flying_over_icy_steppes.png');
width: 141px;
@@ -1329,6 +1339,11 @@
width: 141px;
height: 147px;
}
.background_in_a_painting {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_in_a_painting.png');
width: 141px;
height: 147px;
}
.background_in_an_ancient_tomb {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_in_an_ancient_tomb.png');
width: 141px;
@@ -1389,6 +1404,11 @@
width: 141px;
height: 147px;
}
.background_leafy_tree_tunnel {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_leafy_tree_tunnel.png');
width: 141px;
height: 147px;
}
.background_lighthouse_shore {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_lighthouse_shore.png');
width: 141px;
@@ -1839,6 +1859,11 @@
width: 141px;
height: 147px;
}
.background_springtime_shower {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_springtime_shower.png');
width: 141px;
height: 147px;
}
.background_stable {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_stable.png');
width: 141px;
@@ -2024,6 +2049,11 @@
width: 141px;
height: 147px;
}
.background_under_wisteria {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_under_wisteria.png');
width: 141px;
height: 147px;
}
.background_underwater_among_koi {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_underwater_among_koi.png');
width: 141px;
@@ -2491,6 +2521,11 @@
width: 68px;
height: 68px;
}
.icon_background_cretaceous_forest {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_cretaceous_forest.png');
width: 68px;
height: 68px;
}
.icon_background_crosscountry_ski_trail {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_crosscountry_ski_trail.png');
width: 68px;
@@ -2725,6 +2760,11 @@
width: 68px;
height: 68px;
}
.icon_background_flying_over_hedge_maze {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_flying_over_hedge_maze.png');
width: 68px;
height: 68px;
}
.icon_background_flying_over_icy_steppes {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_flying_over_icy_steppes.png');
width: 68px;
@@ -3000,6 +3040,11 @@
width: 68px;
height: 68px;
}
.icon_background_in_a_painting {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_in_a_painting.png');
width: 68px;
height: 68px;
}
.icon_background_in_an_ancient_tomb {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_in_an_ancient_tomb.png');
width: 68px;
@@ -3060,6 +3105,11 @@
width: 68px;
height: 68px;
}
.icon_background_leafy_tree_tunnel {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_leafy_tree_tunnel.png');
width: 68px;
height: 68px;
}
.icon_background_lighthouse_shore {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_lighthouse_shore.png');
width: 68px;
@@ -3510,6 +3560,11 @@
width: 68px;
height: 68px;
}
.icon_background_springtime_shower {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_springtime_shower.png');
width: 68px;
height: 68px;
}
.icon_background_stable {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_stable.png');
width: 68px;
@@ -3700,6 +3755,11 @@
width: 68px;
height: 68px;
}
.icon_background_under_wisteria {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_under_wisteria.png');
width: 68px;
height: 68px;
}
.icon_background_underwater_among_koi {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_underwater_among_koi.png');
width: 68px;
@@ -18610,6 +18670,11 @@
width: 90px;
height: 90px;
}
.broad_armor_armoire_paintersApron {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_armoire_paintersApron.png');
width: 114px;
height: 90px;
}
.broad_armor_armoire_pirateOutfit {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_armoire_pirateOutfit.png');
width: 114px;
@@ -18835,6 +18900,11 @@
width: 90px;
height: 90px;
}
.head_armoire_beaniePropellerHat {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_beaniePropellerHat.png');
width: 114px;
height: 90px;
}
.head_armoire_bigWig {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_bigWig.png');
width: 90px;
@@ -19095,6 +19165,11 @@
width: 90px;
height: 90px;
}
.head_armoire_paintersBeret {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_paintersBeret.png');
width: 114px;
height: 90px;
}
.head_armoire_paperBag {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_armoire_paperBag.png');
width: 90px;
@@ -19435,6 +19510,11 @@
width: 90px;
height: 90px;
}
.shield_armoire_paintersPalette {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_armoire_paintersPalette.png');
width: 114px;
height: 90px;
}
.shield_armoire_perchingFalcon {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_armoire_perchingFalcon.png');
width: 90px;
@@ -19875,6 +19955,11 @@
width: 68px;
height: 68px;
}
.shop_armor_armoire_paintersApron {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_armor_armoire_paintersApron.png');
width: 68px;
height: 68px;
}
.shop_armor_armoire_pirateOutfit {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_armor_armoire_pirateOutfit.png');
width: 68px;
@@ -20115,6 +20200,11 @@
width: 68px;
height: 68px;
}
.shop_head_armoire_beaniePropellerHat {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_head_armoire_beaniePropellerHat.png');
width: 68px;
height: 68px;
}
.shop_head_armoire_bigWig {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_head_armoire_bigWig.png');
width: 68px;
@@ -20375,6 +20465,11 @@
width: 68px;
height: 68px;
}
.shop_head_armoire_paintersBeret {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_head_armoire_paintersBeret.png');
width: 68px;
height: 68px;
}
.shop_head_armoire_paperBag {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_head_armoire_paperBag.png');
width: 68px;
@@ -20715,6 +20810,11 @@
width: 68px;
height: 68px;
}
.shop_shield_armoire_paintersPalette {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_shield_armoire_paintersPalette.png');
width: 68px;
height: 68px;
}
.shop_shield_armoire_perchingFalcon {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_shield_armoire_perchingFalcon.png');
width: 68px;
@@ -21160,6 +21260,11 @@
width: 68px;
height: 68px;
}
.shop_weapon_armoire_paintbrush {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_weapon_armoire_paintbrush.png');
width: 68px;
height: 68px;
}
.shop_weapon_armoire_paperCutter {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_weapon_armoire_paperCutter.png');
width: 68px;
@@ -21615,6 +21720,11 @@
width: 90px;
height: 90px;
}
.slim_armor_armoire_paintersApron {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_paintersApron.png');
width: 114px;
height: 90px;
}
.slim_armor_armoire_pirateOutfit {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_pirateOutfit.png');
width: 114px;
@@ -22070,6 +22180,11 @@
width: 114px;
height: 90px;
}
.weapon_armoire_paintbrush {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_armoire_paintbrush.png');
width: 114px;
height: 90px;
}
.weapon_armoire_paperCutter {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_armoire_paperCutter.png');
width: 114px;
@@ -27835,6 +27950,61 @@
width: 68px;
height: 68px;
}
.broad_armor_mystery_202304 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_mystery_202304.png');
width: 114px;
height: 90px;
}
.head_mystery_202304 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_mystery_202304.png');
width: 114px;
height: 90px;
}
.set_mystery_202304 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/set_mystery_202304.png');
width: 68px;
height: 68px;
}
.shop_armor_mystery_202304 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_armor_mystery_202304.png');
width: 68px;
height: 68px;
}
.shop_head_mystery_202304 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_head_mystery_202304.png');
width: 68px;
height: 68px;
}
.slim_armor_mystery_202304 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_mystery_202304.png');
width: 114px;
height: 90px;
}
.back_mystery_202305 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/back_mystery_202305.png');
width: 114px;
height: 90px;
}
.headAccessory_mystery_202305 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/headAccessory_mystery_202305.png');
width: 114px;
height: 90px;
}
.shop_back_mystery_202305 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_back_mystery_202305.png');
width: 68px;
height: 68px;
}
.shop_headAccessory_mystery_202305 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_headAccessory_mystery_202305.png');
width: 68px;
height: 68px;
}
.shop_set_mystery_202305 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_set_mystery_202305.png');
width: 68px;
height: 68px;
}
.broad_armor_mystery_301404 {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_mystery_301404.png');
width: 90px;
@@ -58373,11 +58543,6 @@
width: 81px;
height: 99px;
}
.Pet-HatchingPotion-TeaShop {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet-HatchingPotion-TeaShop.png');
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Amber {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_Amber.png');
width: 68px;
@@ -58623,6 +58788,11 @@
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_TeaShop {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_TeaShop.png');
width: 68px;
height: 68px;
}
.Pet_HatchingPotion_Thunderstorm {
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/Pet_HatchingPotion_Thunderstorm.png');
width: 68px;
+6 -2
View File
@@ -19,8 +19,12 @@
top: -16px !important;
}
.Pet.Pet-FlyingPig-Veggie, .Pet.Pet-FlyingPig-Dessert, .Pet.Pet-FlyingPig-VirtualPet {
top: -28px !important;
$foolPets: Veggie, Dessert, VirtualPet, TeaShop;
@each $foolPet in $foolPets {
.Pet.Pet-FlyingPig-#{$foolPet} {
top: -28px !important;
}
}
.Pet[class*="Virtual"] {
+13 -8
View File
@@ -24,9 +24,9 @@
}
}
.icon-16 {
width: 16px;
height: 16px;
.icon-10 {
width: 10px;
height: 10px;
}
.icon-12 {
@@ -34,21 +34,26 @@
height: 12px;
}
.icon-16 {
width: 16px;
height: 16px;
}
.icon-24 {
width: 24px;
height: 24px;
}
.icon-32 {
width: 32px;
height: 32px;
}
.icon-48 {
width: 48px;
height: 48px;
}
.icon-10 {
width: 10px;
height: 10px;
}
.inline {
display: inline-block;
}
+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="b" d="m10,6h6V0h-2v2.72C12.49.99,10.3,0,8,0,3.59,0,0,3.59,0,8s3.59,8,8,8c2.69,0,5.2-1.35,6.68-3.6l-1.67-1.1c-1.11,1.69-2.99,2.71-5.01,2.7-3.31,0-6-2.69-6-6s2.69-6,6-6c1.72,0,3.33.74,4.46,2h-2.46v2Z" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 341 B

@@ -22,6 +22,10 @@
Account created:
<strong>{{ hero.auth.timestamps.created | formatDate }}</strong>
</div>
<div v-if="hero.flags.thirdPartyTools">
User has employed <strong>third party tools</strong>. Last known usage:
<strong>{{ hero.flags.thirdPartyTools | formatDate }}</strong>
</div>
<div v-if="cronError">
"lastCron" value:
<strong>{{ hero.lastCron | formatDate }}</strong>
@@ -46,6 +46,10 @@
Perk offset months:
<strong>{{ hero.purchased.plan.consecutive.offset }}</strong>
</div>
<div>
Perk month count:
<strong>{{ hero.purchased.plan.perkMonthCount }}</strong>
</div>
<div>
Next Mystic Hourglass:
<strong>{{ nextHourglassDate }}</strong>
@@ -149,7 +153,7 @@ export default {
nextHourglassDate () {
const currentPlanContext = getPlanContext(this.hero, new Date());
return currentPlanContext.nextHourglassDate.format('MMMM');
return currentPlanContext.nextHourglassDate.format('MMMM YYYY');
},
},
methods: {
+2 -2
View File
@@ -213,7 +213,7 @@
</a>
<a
class="social-circle"
href="https://twitter.com/habitica"
href="https://twitter.com/habitica/"
target="_blank"
>
<div
@@ -223,7 +223,7 @@
</a>
<a
class="social-circle"
href="https://www.facebook.com/Habitica"
href="https://www.facebook.com/Habitica/"
target="_blank"
>
<div
+1 -1
View File
@@ -244,7 +244,7 @@ export default {
petClass () {
if (some(
this.currentEventList,
event => moment().isBetween(event.start, event.end) && event.aprilFools && event.aprilFools === 'virtual',
event => moment().isBetween(event.start, event.end) && event.aprilFools && event.aprilFools === 'teaShop',
)) {
return this.foolPet(this.member.items.currentPet);
}
@@ -322,6 +322,7 @@ import omit from 'lodash/omit';
import { v4 as uuid } from 'uuid';
import { userStateMixin } from '../../mixins/userState';
import externalLinks from '../../mixins/externalLinks';
import memberSearchDropdown from '@/components/members/memberSearchDropdown';
import closeChallengeModal from './closeChallengeModal';
import Column from '../tasks/column';
@@ -358,7 +359,7 @@ export default {
userLink,
groupLink,
},
mixins: [challengeMemberSearchMixin, userStateMixin],
mixins: [challengeMemberSearchMixin, externalLinks, userStateMixin],
props: ['challengeId'],
data () {
return {
@@ -414,6 +415,10 @@ export default {
mounted () {
if (!this.searchId) this.searchId = this.challengeId;
if (!this.challenge._id) this.loadChallenge();
this.handleExternalLinks();
},
updated () {
this.handleExternalLinks();
},
async beforeRouteUpdate (to, from, next) {
this.searchId = to.params.challengeId;
@@ -120,6 +120,7 @@ import { mapState } from '@/libs/store';
import Sidebar from './sidebar';
import ChallengeItem from './challengeItem';
import challengeModal from './challengeModal';
import externalLinks from '@/mixins/externalLinks';
import challengeUtilities from '@/mixins/challengeUtilities';
import positiveIcon from '@/assets/svg/positive.svg';
@@ -131,7 +132,7 @@ export default {
challengeModal,
MugenScroll,
},
mixins: [challengeUtilities],
mixins: [challengeUtilities, externalLinks],
data () {
return {
loading: true,
@@ -177,6 +178,10 @@ export default {
section: this.$t('challenges'),
});
this.loadChallenges();
this.handleExternalLinks();
},
updated () {
this.handleExternalLinks();
},
methods: {
updateSearch (eventData) {
@@ -81,6 +81,8 @@ import challengeModal from './challengeModal';
import { mapState } from '@/libs/store';
import markdownDirective from '@/directives/markdown';
import externalLinks from '../../mixins/externalLinks';
import challengeItem from './challengeItem';
import challengeIcon from '@/assets/svg/challenge.svg';
@@ -92,6 +94,7 @@ export default {
directives: {
markdown: markdownDirective,
},
mixins: [externalLinks],
props: ['group'],
data () {
return {
@@ -118,6 +121,10 @@ export default {
},
mounted () {
this.loadChallenges();
this.handleExternalLinks();
},
updated () {
this.handleExternalLinks();
},
methods: {
async loadChallenges () {
@@ -145,6 +145,7 @@ import Sidebar from './sidebar';
import ChallengeItem from './challengeItem';
import challengeModal from './challengeModal';
import challengeUtilities from '@/mixins/challengeUtilities';
import externalLinks from '@/mixins/externalLinks';
import challengeIcon from '@/assets/svg/challenge.svg';
import positiveIcon from '@/assets/svg/positive.svg';
@@ -156,7 +157,7 @@ export default {
challengeModal,
MugenScroll,
},
mixins: [challengeUtilities],
mixins: [challengeUtilities, externalLinks],
data () {
return {
icons: Object.freeze({
@@ -203,6 +204,10 @@ export default {
section: this.$t('challenges'),
});
this.loadChallenges();
this.handleExternalLinks();
},
updated () {
this.handleExternalLinks();
},
methods: {
updateSearch (eventData) {
@@ -0,0 +1,209 @@
<template>
<b-modal
id="external-link-modal"
size="md"
>
<!-- HEADER -->
<div slot="modal-header">
<div
class="modal-close"
@click="close()"
>
<div
class="icon-close"
v-html="icons.close"
>
</div>
</div>
<div class="exclamation-container d-flex align-items-center justify-content-center">
<div
v-once
class="svg-icon svg-exclamation"
v-html="icons.exclamation"
></div>
</div>
<h2>
{{ $t('leaveHabitica') }}
</h2>
</div>
<!-- BODY -->
<div
class="row leave-warning-text"
v-html="$t('leaveHabiticaText')"
>
</div>
<div
class="skip-modal"
>
{{ $t('skipExternalLinkModal') }}
</div>
<!-- FOOTER -->
<div slot="modal-footer">
<button
v-once
class="btn btn-primary"
@click="proceed()"
>
{{ $t('continue') }}
</button>
<div
v-once
class="close-link justify-content-center"
@click="close()"
>
{{ $t('cancel') }}
</div>
</div>
</b-modal>
</template>
<style lang="scss">
@import '~@/assets/scss/colors.scss';
#external-link-modal {
&.modal {
display: flex !important;
}
.modal-md {
max-width: 448px;
min-width: 330px;
margin: auto;
.modal-close {
position: absolute;
right: 12px;
top: 12px;
cursor: pointer;
.icon-close {
width: 16px;
height: 16px;
vertical-align: middle;
& svg {
fill: $yellow-1;
opacity: 0.75;
}
& :hover {
fill: $yellow-1;
opacity: 1;
}
}
}
.modal-content {
background: transparent;
}
.modal-header {
justify-content: center;
padding-top: 32px;
padding-bottom: 0px;
background: $yellow-100;
border-top-right-radius: 8px;
border-top-left-radius: 8px;
border-bottom: none;
.exclamation-container {
width: 64px;
height: 64px;
border-radius: 50%;
background: $yellow-1;
margin: 0 auto;
margin-bottom: 16px;
}
.svg-exclamation {
width: 8px;
color: $white;
}
h2 {
color: $yellow-1;
margin-bottom: 16px;
}
}
.modal-body {
padding: 16px 44px 20px 44px;
background: $white;
.leave-warning-text {
font-size: 0.875rem;
line-height: 1.71;
text-align: center;
margin-top:24px;
}
.skip-modal {
color: $gray-100;
font-size: 0.75rem;
text-align: center;
line-height: 1.33;
margin-top: 16px;
// padding-bottom: 24px;
}
}
.modal-footer {
background: $white;
border-bottom-right-radius: 8px;
border-bottom-left-radius: 8px;
justify-content: center;
border-top: none;
padding-top: 0;
}
.close-link {
color: $purple-300;
line-height: 1.71;
font-size: 0.875rem;
cursor: pointer;
margin-top:16px;
margin-bottom: 8px;
text-align: center;
&:hover {
text-decoration: underline;
}
}
}
}
</style>
<script>
import exclamationIcon from '@/assets/svg/exclamation.svg';
import closeIcon from '@/assets/svg/new-close.svg';
export default {
data () {
return {
icons: Object.freeze({
close: closeIcon,
exclamation: exclamationIcon,
}),
url: '',
};
},
mounted () {
this.$root.$on('habitica:external-link', url => {
this.url = url;
this.$root.$emit('bv::show::modal', 'external-link-modal');
});
},
beforeDestroy () {
this.$root.$off('habitica:external-link');
},
methods: {
close () {
this.$root.$emit('bv::hide::modal', 'external-link-modal');
},
proceed () {
window.open(this.url, '_blank').focus();
this.close();
},
},
};
</script>
@@ -87,6 +87,8 @@
<script>
import debounce from 'lodash/debounce';
import externalLinks from '../../mixins/externalLinks';
import autocomplete from '../chat/autoComplete';
import communityGuidelines from './communityGuidelines';
import chatMessage from '../chat/chatMessages';
@@ -103,6 +105,7 @@ export default {
communityGuidelines,
chatMessage,
},
mixins: [externalLinks],
props: ['label', 'group', 'placeholder'],
data () {
return {
@@ -132,6 +135,10 @@ export default {
},
mounted () {
this.textbox = this.$refs['user-entry'];
this.handleExternalLinks();
},
updated () {
this.handleExternalLinks();
},
methods: {
// https://medium.com/@_jh3y/how-to-where-s-the-caret-getting-the-xy-position-of-the-caret-a24ba372990a
@@ -11,9 +11,12 @@
<div class="quest_screen"></div>
<div class="row heading">
<div class="col-12 text-center pr-5 pl-5">
<h2 v-once>
<h1
v-once
class="mb-2"
>
{{ $t('playInPartyTitle') }}
</h2>
</h1>
<p
v-once
class="mb-4"
@@ -22,67 +25,91 @@
</p>
<button
v-once
class="btn btn-primary"
class="btn btn-primary px-4 mb-2"
@click="createParty()"
>
{{ $t('createParty') }}
</button>
</div>
</div>
<close-x
@close="close()"
/>
</div>
<div class="row grey-row">
<div class="col-12 text-center">
<div class="col-12 text-center px-0">
<div class="join-party"></div>
<h2 v-once>
{{ $t('wantToJoinPartyTitle') }}
</h2>
<p v-html="$t('wantToJoinPartyDescription')"></p>
<div
class="form-group"
@click="copyUsername"
<h1
v-once
class="mb-2"
>
<div class="d-flex align-items-center">
<label
v-once
class="mr-3"
>{{ $t('username') }}</label>
<div class="flex-grow-1">
<div class="input-group-prepend input-group-text">
@
<div class="text">
{{ user.auth.local.username }}
</div>
<div
class="svg-icon copy-icon"
v-html="icons.copy"
></div>
<div
v-once
class="small"
>
{{ $t('copy') }}
</div>
</div>
{{ $t('wantToJoinPartyTitle') }}
</h1>
<p
v-once
class="mb-4"
v-html="$t('partyFinderDescription')"
>
</p>
<div
v-if="seeking"
>
<div
class="green-bar mb-3"
>
{{ $t('currentlyLookingForParty') }}
</div>
<div class="d-flex justify-content-center">
<div
class="red-link"
@click="seekParty()"
>
{{ $t('leave') }}
</div>
</div>
</div>
<button
v-else
class="btn btn-primary px-4 mt-2 mb-1"
@click="seekParty()"
>
{{ $t('lookForParty') }}
</button>
</div>
</div>
</b-modal>
</template>
<style>
#create-party-modal .modal-body {
padding: 0rem 0.75rem;
}
<style lang="scss">
#create-party-modal {
display: flex !important;
overflow-y: hidden;
#create-party-modal .modal-dialog {
width: 35.75rem;
}
@media (max-height: 770px) {
overflow-y: auto;
}
#create-party-modal .modal-header {
padding: 0;
border-bottom: 0px;
.modal-body {
padding: 0rem 0.75rem;
}
.modal-content {
border-radius: 8px;
}
.modal-dialog {
width: 566px;
margin: auto;
@media (max-height: 826px) {
margin-top: 56px;
}
}
.modal-header {
padding: 0;
border-bottom: 0px;
}
}
</style>
@@ -107,15 +134,27 @@
cursor: pointer;
}
.green-bar {
height: 32px;
font-size: 14px;
font-weight: bold;
line-height: 1.71;
text-align: center;
color: $green-1;
background-color: $green-100;
border-radius: 2px;
padding: 4px 0px 4px 0px;
}
.grey-row {
background-color: $gray-700;
color: #4e4a57;
padding: 2em;
border-radius: 0px 0px 2px 2px;
border-radius: 0px 0px 8px 8px;
}
h2 {
color: $gray-100;
h1 {
color: $purple-300;
}
.header-wrap {
@@ -132,10 +171,6 @@
border-radius: 2px 2px 0 0;
image-rendering: optimizequality;
}
h2 {
color: $purple-200;
}
}
.heading {
@@ -182,6 +217,21 @@
margin: 0.75rem auto 0.75rem 0.25rem;
}
p {
line-height: 1.71;
}
.red-link {
cursor: pointer;
font-size: 14px;
line-height: 1.71;
text-align: center;
color: $maroon-50;
&:hover {
text-decoration: underline;
}
}
.small {
color: $gray-200;
margin: auto 0.5rem auto 0.25rem;
@@ -192,21 +242,29 @@
import { mapState } from '@/libs/store';
import * as Analytics from '@/libs/analytics';
import notifications from '@/mixins/notifications';
import closeX from '../ui/closeX';
import copyIcon from '@/assets/svg/copy.svg';
export default {
components: {
closeX,
},
mixins: [notifications],
data () {
return {
icons: Object.freeze({
copy: copyIcon,
}),
seeking: false,
};
},
computed: {
...mapState({ user: 'user.data' }),
},
mounted () {
this.seeking = Boolean(this.user.party.seeking);
},
methods: {
async createParty () {
const group = {
@@ -223,7 +281,10 @@ export default {
});
this.$root.$emit('bv::hide::modal', 'create-party-modal');
this.$router.push('/party');
await this.$router.push('/party');
},
close () {
this.$root.$emit('bv::hide::modal', 'create-party-modal');
},
copyUsername () {
if (navigator.clipboard) {
@@ -238,6 +299,12 @@ export default {
}
this.text(this.$t('usernameCopied'));
},
seekParty () {
this.$store.dispatch('user:set', {
'party.seeking': !this.user.party.seeking ? new Date() : null,
});
this.seeking = !this.seeking;
},
},
};
</script>
@@ -542,7 +542,8 @@ export default {
await this.$store.dispatch('guilds:leave', data);
if (this.isParty) {
this.$router.push({ name: 'tasks' });
await this.$router.push({ name: 'tasks' });
window.location.reload(true);
}
},
upgradeGroup () {
@@ -4,12 +4,12 @@
<group-plan-creation-modal />
<div>
<div class="header">
<h1 class="text-center">
Need more for your Group?
<h1 v-once class="text-center">
{{ $t('groupPlanTitle') }}
</h1>
<div class="row">
<div class="col-8 offset-2 text-center">
<h2 class="sub-text">
<h2 v-once class="sub-text">
{{ $t('groupBenefitsDescription') }}
</h2>
</div>
@@ -24,8 +24,8 @@
src="~@/assets/images/group-plans/group-14@3x.png"
>
<hr>
<h2>{{ $t('teamBasedTasks') }}</h2>
<p>Set up an easily-viewed shared task list for the group. Assign tasks to your fellow group members, or let them claim their own tasks to make it clear what everyone is working on!</p><!-- eslint-disable-line max-len -->
<h2 v-once> {{ $t('teamBasedTasks') }} </h2>
<p v-once> {{ $t('teamBasedTasksListDesc') }} </p>
</div>
</div>
<div class="col-4">
@@ -35,8 +35,8 @@
src="~@/assets/images/group-plans/group-12@3x.png"
>
<hr>
<h2>Group Management Controls</h2>
<p>Use task approvals to verify that a task that was really completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.</p><!-- eslint-disable-line max-len -->
<h2 v-once> {{ $t('groupManagementControls') }} </h2>
<p v-once> {{ $t('groupManagementControlsDesc') }} </p>
</div>
</div>
<div class="col-4">
@@ -46,8 +46,8 @@
src="~@/assets/images/group-plans/group-13@3x.png"
>
<hr>
<h2>In-Game Benefits</h2>
<p>Group members get an exclusive Jackalope Mount, as well as full subscription benefits, including special monthly equipment sets and the ability to buy gems with gold.</p><!-- eslint-disable-line max-len -->
<h2 v-once> {{ $t('inGameBenefits') }} </h2>
<p v-once> {{ $t('inGameBenefitsDesc') }} </p>
</div>
</div>
</div>
@@ -0,0 +1,343 @@
<template>
<div>
<div class="d-flex justify-content-center">
<div
v-if="seekers.length > 0"
class="fit-content mx-auto mt-4"
>
<div class="d-flex align-items-center">
<h1 v-once class="my-auto mr-auto"> {{ $t('findPartyMembers') }}</h1>
<div
class="btn btn-secondary btn-sync ml-auto my-auto pl-2 pr-3 d-flex"
@click="refreshList()"
>
<div class="svg-icon icon-16 color my-auto mr-2" v-html="icons.sync"></div>
<div class="ml-auto"> {{ $t('refreshList') }} </div>
</div>
</div>
<div class="d-flex flex-wrap seeker-list">
<div
v-for="(seeker, index) in seekers"
:key="seeker._id"
class="seeker"
>
<div class="d-flex">
<avatar
:member="seeker"
:hideClassBadge="true"
@click.native="showMemberModal(seeker._id)"
class="mr-3 mb-2"
/>
<div class="card-data">
<user-link
:user-id="seeker._id"
:name="seeker.profile.name"
:backer="seeker.backer"
:contributor="seeker.contributor"
/>
<div class="small-with-border pb-2 mb-2">
@{{ seeker.auth.local.username }} {{ $t('level') }} {{ seeker.stats.lvl }}
</div>
<div
class="d-flex"
>
<strong v-once> {{ $t('classLabel') }} </strong>
<span
class="svg-icon d-inline-block icon-16 my-auto mx-2"
v-html="icons[seeker.stats.class]"
>
</span>
<strong
:class="`${seeker.stats.class}-color`"
>
{{ $t(seeker.stats.class) }}
</strong>
</div>
<div>
<strong v-once class="mr-2"> {{ $t('checkinsLabel') }} </strong>
{{ seeker.loginIncentives }}
</div>
<div>
<strong v-once class="mr-2"> {{ $t('languageLabel') }} </strong>
{{ displayLanguage(seeker.preferences.language) }}
</div>
</div>
</div>
<strong
v-if="!seeker.invited"
@click="inviteUser(seeker._id, index)"
class="btn btn-primary w-100"
>
{{ $t('inviteToParty') }}
</strong>
<div
v-else
@click="rescindInvite(seeker._id, index)"
class="btn btn-success w-100"
v-html="$t('invitedToYourParty')"
>
</div>
</div>
<mugen-scroll
v-show="loading"
:handler="infiniteScrollTrigger"
:should-handle="!loading && canLoadMore"
:threshold="1"
/>
</div>
</div>
<div
v-if="seekers.length === 0 && !loading"
class="d-flex flex-column empty-state text-center my-5"
>
<div class="gray-circle mb-3 mx-auto d-flex">
<div
class="svg-icon icon-32 color m-auto"
v-html="icons.users"
>
</div>
</div>
<strong class="mb-1"> {{ $t('findMorePartyMembers') }} </strong>
<div v-html="$t('noOneLooking')"></div>
</div>
</div>
<h2
v-show="loading"
class="loading"
:class="seekers.length === 0 ? 'mt-3' : 'mt-0'"
>
{{ $t('loading') }}
</h2>
</div>
</template>
<style lang="scss" scoped>
@import '~@/assets/scss/colors.scss';
h1 {
color: $purple-300;
}
strong {
line-height: 1.71;
}
.avatar {
background-color: $gray-600;
}
.btn-success {
box-shadow: none;
color: $green-1;
font-weight: normal;
&:not(:disabled):not(.disabled):active {
color: $green-1;
}
}
.btn-sync {
min-width: 128px;
max-height: 32px;
.svg-icon {
color: $gray-200;
}
}
.card-data {
width: 267px;
}
.empty-state {
color: $gray-100;
line-height: 1.71;
}
.fit-content {
width: fit-content;
}
.gray-circle {
width: 64px;
height: 64px;
color: $gray-600;
background-color: $gray-200;
border-radius: 100px;
.icon-32 {
height: auto;
}
}
.loading {
text-align: center;
color: $purple-300;
}
.seeker-list {
max-width: 920px;
@media (max-width: 962px) {
max-width: 464px;
};
.seeker {
width: 448px;
margin-bottom: 24px;
padding: 8px;
border-radius: 4px;
box-shadow: 0 1px 3px 0 rgba(26, 24, 29, 0.12), 0 1px 2px 0 rgba(26, 24, 29, 0.24);
&:first-of-type {
margin-top: 24px;
}
@media (min-width: 963px) {
&:nth-child(2) {
margin-top: 24px;
}
&:nth-child(even) {
margin-left: 24px;
}
}
}
}
.small-with-border {
border-bottom: 1px solid $gray-500;
color: $gray-100;
font-size: 12px;
font-weight: normal;
line-height: 1.33;
}
.healer-color {
color: $yellow-10;
}
.rogue-color {
color: $purple-200;
}
.warrior-color {
color: $red-50;
}
.wizard-color {
color: $blue-10;
}
</style>
<script>
import debounce from 'lodash/debounce';
import MugenScroll from 'vue-mugen-scroll';
import Avatar from '../avatar';
import userLink from '../userLink';
import { mapState } from '@/libs/store';
import syncIcon from '@/assets/svg/sync-2.svg';
import usersIcon from '@/assets/svg/users.svg';
import warriorIcon from '@/assets/svg/warrior.svg';
import rogueIcon from '@/assets/svg/rogue.svg';
import healerIcon from '@/assets/svg/healer.svg';
import wizardIcon from '@/assets/svg/wizard.svg';
export default {
components: {
Avatar,
MugenScroll,
userLink,
},
data () {
return {
canLoadMore: true,
loading: true,
page: 0,
party: {},
seekers: [],
icons: Object.freeze({
warrior: warriorIcon,
rogue: rogueIcon,
healer: healerIcon,
sync: syncIcon,
users: usersIcon,
wizard: wizardIcon,
}),
};
},
computed: {
...mapState({
availableLanguages: 'i18n.availableLanguages',
user: 'user.data',
}),
},
async mounted () {
try {
this.party = await this.$store.dispatch('guilds:getGroup', { groupId: this.user.party._id });
} catch {
this.$router.push('/');
}
if (!this.party._id || this.party.leader._id !== this.user._id) {
this.$router.push('/');
} else {
this.$store.dispatch('common:setTitle', {
section: this.$t('lookingForPartyTitle'),
});
this.seekers = await this.$store.dispatch('party:lookingForParty');
this.canLoadMore = this.seekers.length === 30;
this.loading = false;
}
},
methods: {
displayLanguage (languageCode) {
const language = this.availableLanguages.find(lang => lang.code === languageCode);
if (language) {
return language.name;
}
return languageCode;
},
infiniteScrollTrigger () {
if (this.canLoadMore) {
this.loading = true;
}
this.loadMore();
},
async inviteUser (userId, index) {
await this.$store.dispatch('guilds:invite', {
invitationDetails: {
inviter: this.user.profile.name,
uuids: [userId],
},
groupId: this.party._id,
});
this.seekers[index].invited = true;
},
loadMore: debounce(async function loadMoreDebounce () {
this.page += 1;
const addlSeekers = await this.$store.dispatch('party:lookingForParty', { page: this.page });
this.seekers = this.seekers.concat(addlSeekers);
this.canLoadMore = this.seekers.length % 30 === 0;
this.loading = false;
}, 1000),
async refreshList () {
this.loading = true;
this.page = 0;
this.seekers = await this.$store.dispatch('party:lookingForParty');
this.canLoadMore = this.seekers.length === 30;
this.loading = false;
},
async rescindInvite (userId, index) {
await this.$store.dispatch('members:removeMember', {
memberId: userId,
groupId: this.party._id,
});
this.seekers[index].invited = false;
},
showMemberModal (userId) {
this.$router.push({ name: 'userProfile', params: { userId } });
},
},
};
</script>
@@ -2,7 +2,7 @@
<div class="sidebar px-4">
<div>
<div class="buttons-wrapper">
<div class="button-container button-with-menu-row">
<div class="button-container d-flex">
<button
v-if="!isMember"
class="btn btn-success btn-success"
@@ -203,10 +203,6 @@ export default {
}
}
.button-with-menu-row {
display: flex;
}
.menuIcon {
width: 4px;
height: 1rem;
+21 -6
View File
@@ -51,20 +51,20 @@
</div>
<div
v-else
class="no-party d-none d-md-flex justify-content-center text-center mr-4"
class="no-party d-none d-md-flex justify-content-center text-center mr-4"
>
<div class="align-self-center">
<h3>{{ $t('battleWithFriends') }}</h3>
<h3>{{ user.party._id ? $t('questWithOthers') : $t('battleWithFriends') }}</h3>
<span
class="small-text"
v-html="$t('inviteFriendsParty')"
v-html="user.party._id ? $t('inviteFriendsParty') : $t('startPartyDetail')"
></span>
<br>
<button
class="btn btn-primary"
@click="createOrInviteParty()"
>
{{ user.party._id ? $t('inviteFriends') : $t('startAParty') }}
{{ user.party._id ? $t('findPartyMembers') : $t('getStarted') }}
</button>
</div>
</div>
@@ -122,6 +122,7 @@
<script>
import orderBy from 'lodash/orderBy';
import * as Analytics from '@/libs/analytics';
import { mapGetters, mapActions } from '@/libs/store';
import MemberDetails from '../memberDetails';
import createPartyModal from '../groups/createPartyModal';
@@ -232,10 +233,24 @@ export default {
this.expandedMember = memberId;
}
},
createOrInviteParty () {
async createOrInviteParty () {
if (this.user.party._id) {
this.$root.$emit('inviteModal::inviteToGroup', this.user.party);
await Analytics.track({
eventName: 'Header Party CTA',
eventAction: 'Header Party CTA',
eventCategory: 'behavior',
hitType: 'event',
state: 'Find Party Members',
});
this.$router.push('/looking-for-party');
} else {
await Analytics.track({
eventName: 'Header Party CTA',
eventAction: 'Header Party CTA',
eventCategory: 'behavior',
hitType: 'event',
state: 'Get Started',
});
this.$root.$emit('bv::show::modal', 'create-party-modal');
}
},
+44 -3
View File
@@ -148,7 +148,7 @@
</div>
</li>
<b-nav-item
v-if="user.party._id"
v-if="user.party._id && user._id !== partyLeaderId"
class="topbar-item"
:class="{'active': $route.path.startsWith('/party')}"
tag="li"
@@ -156,6 +156,36 @@
>
{{ $t('party') }}
</b-nav-item>
<li
v-if="user.party._id && user._id === partyLeaderId"
class="topbar-item droppable"
:class="{'active': $route.path.startsWith('/party')}"
>
<div
class="chevron rotate"
@click="dropdownMobile($event)"
>
<div
v-once
class="chevron-icon-down"
v-html="icons.chevronDown"
></div>
</div>
<router-link
class="nav-link"
:to="{name: 'party'}"
>
{{ $t('party') }}
</router-link>
<div class="topbar-dropdown">
<router-link
class="topbar-dropdown-item dropdown-item"
:to="{name: 'lookingForParty'}"
>
{{ $t('lookingForPartyTitle') }}
</router-link>
</div>
</li>
<b-nav-item
v-if="!user.party._id"
class="topbar-item"
@@ -768,6 +798,7 @@ export default {
return {
isUserDropdownOpen: false,
menuIsOpen: false,
partyLeaderId: null,
icons: Object.freeze({
gem: gemIcon,
gold: goldIcon,
@@ -796,8 +827,9 @@ export default {
};
},
},
mounted () {
this.getUserGroupPlans();
async mounted () {
await this.getUserGroupPlans();
await this.getUserParty();
Array.from(document.getElementById('menu_collapse').getElementsByTagName('a')).forEach(link => {
link.addEventListener('click', this.closeMenu);
});
@@ -805,6 +837,9 @@ export default {
link.addEventListener('mouseenter', this.dropdownDesktop);
link.addEventListener('mouseleave', this.dropdownDesktop);
});
this.$root.$on('update-party', () => {
this.getUserParty();
});
},
methods: {
modForm () {
@@ -816,6 +851,12 @@ export default {
async getUserGroupPlans () {
await this.$store.dispatch('guilds:getGroupPlans');
},
async getUserParty () {
if (this.user.party._id) {
await this.$store.dispatch('party:getParty');
this.partyLeaderId = this.$store.state.party.data.leader._id;
}
},
openPartyModal () {
this.$root.$emit('bv::show::modal', 'create-party-modal');
},
@@ -34,11 +34,13 @@
<script>
import BaseNotification from './base';
import { mapState } from '@/libs/store';
import sync from '@/mixins/sync';
export default {
components: {
BaseNotification,
},
mixins: [sync],
props: {
notification: {
type: Object,
@@ -73,6 +75,7 @@ export default {
}
await this.$store.dispatch('guilds:join', { groupId: group.id, type: 'party' });
this.sync();
this.$router.push('/party');
},
reject () {
@@ -171,8 +171,9 @@ export default {
getPetItemClass () {
if (this.isOwned() && some(
this.currentEventList,
event => moment().isBetween(event.start, event.end) && event.aprilFools && event.aprilFools === 'virtual',
event => moment().isBetween(event.start, event.end) && event.aprilFools && event.aprilFools === 'teaShop',
)) {
if (this.isSpecial()) return `Pet ${this.foolPet(this.item.key)}`;
const petString = `${this.item.eggKey}-${this.item.key}`;
return `Pet ${this.foolPet(petString)}`;
}
@@ -139,6 +139,8 @@
import axios from 'axios';
import moment from 'moment';
import externalLinks from '../../mixins/externalLinks';
import renderWithMentions from '@/libs/renderWithMentions';
import { mapState } from '@/libs/store';
import userLink from '../userLink';
@@ -150,6 +152,7 @@ export default {
components: {
userLink,
},
mixins: [externalLinks],
filters: {
timeAgo (value) {
return moment(value).fromNow();
@@ -179,6 +182,10 @@ export default {
},
mounted () {
this.$emit('message-card-mounted');
this.handleExternalLinks();
},
updated () {
this.handleExternalLinks();
},
methods: {
report () {
+1 -27
View File
@@ -23,33 +23,7 @@
</div>
<div class="section">
<h3>{{ $t('thirdPartyApps') }}</h3>
<ul>
<li>
<a
target="_blank"
href="https://www.beeminder.com/habitica"
>{{ $t('beeminder') }}</a>
<br>
{{ $t('beeminderDesc') }}
</li>
<li>
<div v-html="$t('chatExtension')">
</div>
<span>{{ $t('chatExtensionDesc') }}</span>
</li>
<li>
<a
target="_blank"
:href="`https://oldgods.net/habitica/habitrpg_user_data_display.html?uuid=` + user._id"
>{{ $t('dataDisplayTool') }}</a>
<br>
{{ $t('dataToolDesc') }}
</li>
<li>
<div v-html="$t('otherExtensions')"></div>
<span>{{ $t('otherDesc') }}</span>
</li>
</ul>
<p v-html="$t('thirdPartyTools')"></p>
<hr>
</div>
</div>
@@ -179,7 +179,9 @@ export default {
let valid = true;
for (const stat of canRestore) {
if (this.restoreValues.stats[stat] === '') {
if (this.restoreValues.stats[stat] === ''
|| this.restoreValues.stats[stat] < 0
) {
this.restoreValues.stats[stat] = this.user.stats[stat];
valid = false;
}
@@ -804,7 +804,7 @@ export default {
return currentPlanContext.nextHourglassDate;
},
nextHourGlass () {
const nextHourglassMonth = this.nextHourGlassDate.format('MMM');
const nextHourglassMonth = this.nextHourGlassDate.format('MMM YYYY');
return nextHourglassMonth;
},
+39 -15
View File
@@ -17,6 +17,19 @@
class="faq-question"
>
<h2
v-once
v-if="index === 0"
>
{{ $t('general') }}
</h2>
<h2
v-once
v-if="entry.heading === 'party-with-friends'"
id="parties"
>
{{ $t('parties') }}
</h2>
<h3
v-once
v-b-toggle="entry.heading"
role="tab"
@@ -24,7 +37,7 @@
@click="handleClick($event)"
>
{{ entry.question }}
</h2>
</h3>
<b-collapse
:id="entry.heading"
:visible="isVisible(entry.heading)"
@@ -49,25 +62,36 @@
</template>
<style lang='scss' scoped>
.card-body {
margin-bottom: 1em;
h2 {
color: #34313a;
border-bottom: 1px solid #e1e0e3;
margin-top: 24px;
padding-bottom: 16px;
}
.faq-question h2 {
cursor: pointer;
}
.faq-question {
a {
text-decoration: none;
color: #4F2A93;
}
.faq-question .card-body {
padding: 0;
}
h3 {
font-size: 16px;
font-weight: normal;
line-height: 1.75;
cursor: pointer;
.static-wrapper .faq-question h2 {
margin: 0 0 16px 0;
}
&:hover {
text-decoration: underline;
}
}
.faq-question a {
text-decoration: none;
color: #4F2A93;
.card-body {
padding: 0;
font-size: 14px;
line-height: 1.71;
margin-bottom: 1em;
}
}
@media only screen and (max-width: 768px) {
@@ -354,6 +354,9 @@
<style lang='scss'>
@import '~@/assets/scss/static.scss';
#front .form-text a {
color: $white !important;
}
</style>
<style lang="scss" scoped>
@@ -362,10 +365,6 @@
@import url('https://fonts.googleapis.com/css?family=Varela+Round');
#front {
.form-text a {
color: $white !important;
}
.container-fluid {
margin: 0;
}
@@ -165,10 +165,6 @@ export default {
question: 'pkQuestion7',
answer: 'pkAnswer7',
},
{
question: 'pkQuestion8',
answer: 'pkAnswer8',
},
],
});
},
@@ -198,10 +198,6 @@
color: $purple-200;
}
li, p {
font-size: 16px;
}
.media img {
margin: 1em;
}
@@ -355,6 +355,7 @@ import Task from './task';
import ClearCompletedTodos from './clearCompletedTodos';
import buyMixin from '@/mixins/buy';
import sync from '@/mixins/sync';
import externalLinks from '@/mixins/externalLinks';
import { mapState, mapActions, mapGetters } from '@/libs/store';
import shopItem from '../shops/shopItem';
import BuyQuestModal from '@/components/shops/quests/buyQuestModal.vue';
@@ -384,7 +385,7 @@ export default {
shopItem,
draggable,
},
mixins: [buyMixin, notifications, sync],
mixins: [buyMixin, notifications, sync, externalLinks],
// @TODO Set default values for props
// allows for better control of props values
// allows for better control of where this component is called
@@ -534,6 +535,10 @@ export default {
if (this.activeFilter.label !== 'complete2') return;
this.loadCompletedTodos();
});
this.handleExternalLinks();
},
updated () {
this.handleExternalLinks();
},
beforeDestroy () {
this.$root.$off('buyModal::boughtItem');
@@ -6,6 +6,7 @@
'groupTask': task.group.id,
'task-not-editable': !teamManagerAccess,
'task-not-scoreable': showTaskLockIcon,
'link-exempt': !isChallengeTask && !isGroupTask,
}, `type_${task.type}`
]"
@click="castEnd($event, task)"
@@ -83,6 +83,7 @@
<script>
import moment from 'moment';
import { mapState } from '@/libs/store';
import externalLinks from '@/mixins/externalLinks';
import scoreTask from '@/mixins/scoreTask';
import sync from '@/mixins/sync';
import Task from './task';
@@ -93,7 +94,7 @@ export default {
Task,
LoadingSpinner,
},
mixins: [scoreTask, sync],
mixins: [externalLinks, scoreTask, sync],
props: {
yesterDailies: {
type: Array,
@@ -108,6 +109,11 @@ export default {
dueDate: moment().subtract(1, 'days'),
};
},
updated () {
window.setTimeout(() => {
this.handleExternalLinks();
}, 500);
},
computed: {
...mapState({ user: 'user.data' }),
tasksByType () {
@@ -0,0 +1,46 @@
<template>
<div
class="modal-close"
@click="$emit('close')"
>
<div
class="svg-icon svg-close color"
v-html="icons.close"
>
</div>
</div>
</template>
<style lang="scss" scoped>
.modal-close {
position: absolute;
right: 16px;
top: 16px;
cursor: pointer;
.svg-close {
width: 18px;
height: 18px;
vertical-align: middle;
opacity: 0.75;
&:hover {
opacity: 1;
}
}
}
</style>
<script>
import close from '@/assets/svg/close.svg';
export default {
data () {
return {
icons: Object.freeze({
close,
}),
};
},
};
</script>
+10 -5
View File
@@ -2,13 +2,13 @@
<router-link
v-if="displayName"
v-b-tooltip.hover.top="tierTitle"
class="leader user-link"
class="leader user-link d-flex"
:to="{'name': 'userProfile', 'params': {'userId': id}}"
:class="levelStyle()"
>
{{ displayName }}
<div
class="svg-icon"
class="svg-icon icon-12"
v-html="tierIcon()"
></div>
</router-link>
@@ -37,10 +37,15 @@
color: $gray-50;
}
&[class*="tier"] .svg-icon {
margin-top: 5px;
}
&.npc .svg-icon {
margin-top: 4px;
}
.svg-icon {
width: 10px;
display: inline-block;
margin-left: .5em;
margin-left: 6px;
&:empty {
display: none;
@@ -759,6 +759,7 @@ import challenge from '@/assets/svg/challenge.svg';
import member from '@/assets/svg/member-icon.svg';
import staff from '@/assets/svg/tier-staff.svg';
import error404 from '../404';
import externalLinks from '../../mixins/externalLinks';
import { userCustomStateMixin } from '../../mixins/userState';
// @TODO: EMAILS.COMMUNITY_MANAGER_EMAIL
const COMMUNITY_MANAGER_EMAIL = 'admin@habitica.com';
@@ -772,7 +773,7 @@ export default {
profileStats,
error404,
},
mixins: [userCustomStateMixin('userLoggedIn')],
mixins: [externalLinks, userCustomStateMixin('userLoggedIn')],
props: ['userId', 'startingPage'],
data () {
return {
@@ -862,8 +863,12 @@ export default {
mounted () {
this.loadUser();
this.oldTitle = this.$store.state.title;
this.handleExternalLinks();
this.selectPage(this.startingPage);
},
updated () {
this.handleExternalLinks();
},
beforeDestroy () {
if (this.oldTitle) {
this.$store.dispatch('common:setTitle', {
+7 -1
View File
@@ -33,9 +33,15 @@ setUpLogging();
setupAnalytics(); // just create queues for analytics, no scripts loaded at this time
const store = getStore();
export default new Vue({
const vueInstance = new Vue({
el: '#app',
router,
store,
render: h => h(AppComponent),
});
export default vueInstance;
window.externalLink = url => {
vueInstance.$root.$emit('habitica:external-link', url);
};
@@ -0,0 +1,36 @@
import some from 'lodash/some';
export default {
methods: {
handleExternalLinks () {
const { TRUSTED_DOMAINS } = process.env;
const allLinks = document.getElementsByTagName('a');
for (let i = 0; i < allLinks.length; i += 1) {
const link = allLinks[i];
let domainIndex = link.href.indexOf('www');
if (domainIndex !== -1 && domainIndex < 9) {
domainIndex += 4;
} else {
domainIndex = link.href.indexOf('//') + 2;
}
if ((link.classList.value.indexOf('external-link') === -1)
&& (!link.offsetParent || link.offsetParent.classList.value.indexOf('link-exempt') === -1)
&& domainIndex !== 1
&& !some(TRUSTED_DOMAINS.split(','), domain => link.href.indexOf(domain) === domainIndex)) {
link.classList.add('external-link');
link.addEventListener('click', e => {
if (e.ctrlKey || e.metaKey) {
return;
}
e.stopPropagation();
e.preventDefault();
window.externalLink(link.href);
});
}
}
},
},
};
+4 -4
View File
@@ -40,15 +40,15 @@ export default {
'Dragon',
'Cactus',
];
if (!pet) return 'Pet-Cactus-Virtual';
if (!pet) return 'Pet-TigerCub-TeaShop';
if (SPECIAL_PETS.indexOf(pet) !== -1) {
return 'Pet-Wolf-Virtual';
return 'Pet-Dragon-TeaShop';
}
const species = pet.slice(0, pet.indexOf('-'));
if (includes(BASE_PETS, species)) {
return `Pet-${species}-Virtual`;
return `Pet-${species}-TeaShop`;
}
return 'Pet-Fox-Virtual';
return 'Pet-BearCub-TeaShop';
},
},
};
+17 -1
View File
@@ -1,5 +1,6 @@
import Vue from 'vue';
import VueRouter from 'vue-router';
import * as Analytics from '@/libs/analytics';
import getStore from '@/store';
import handleRedirect from './handleRedirect';
@@ -72,13 +73,14 @@ const ItemsPage = () => import(/* webpackChunkName: "inventory" */'@/components/
const EquipmentPage = () => import(/* webpackChunkName: "inventory" */'@/components/inventory/equipment/index');
const StablePage = () => import(/* webpackChunkName: "inventory" */'@/components/inventory/stable/index');
// Guilds
// Guilds & Parties
const GuildIndex = () => import(/* webpackChunkName: "guilds" */ '@/components/groups/index');
const TavernPage = () => import(/* webpackChunkName: "guilds" */ '@/components/groups/tavern');
const MyGuilds = () => import(/* webpackChunkName: "guilds" */ '@/components/groups/myGuilds');
const GuildsDiscoveryPage = () => import(/* webpackChunkName: "guilds" */ '@/components/groups/discovery');
const GroupPage = () => import(/* webpackChunkName: "guilds" */ '@/components/groups/group');
const GroupPlansAppPage = () => import(/* webpackChunkName: "guilds" */ '@/components/groups/groupPlan');
const LookingForParty = () => import(/* webpackChunkName: "guilds" */ '@/components/groups/lookingForParty');
// Group Plans
const GroupPlanIndex = () => import(/* webpackChunkName: "group-plans" */ '@/components/group-plans/index');
@@ -157,6 +159,7 @@ const router = new VueRouter({
],
},
{ name: 'party', path: '/party', component: GroupPage },
{ name: 'lookingForParty', path: '/looking-for-party', component: LookingForParty },
{ name: 'groupPlan', path: '/group-plans', component: GroupPlansAppPage },
{
name: 'groupPlanDetail',
@@ -433,6 +436,19 @@ router.beforeEach(async (to, from, next) => {
}
}
if (to.name === 'party') {
router.app.$root.$emit('update-party');
}
if (to.name === 'lookingForParty') {
Analytics.track({
hitType: 'event',
eventName: 'View Find Members',
eventAction: 'View Find Members',
eventCategory: 'behavior',
}, { trackOnClient: true });
}
// Redirect old guild urls
if (to.hash.indexOf('#/options/groups/guilds/') !== -1) {
const splits = to.hash.split('/');
+12
View File
@@ -1,3 +1,4 @@
import axios from 'axios';
import { loadAsyncResource } from '@/libs/asyncResource';
export function getMembers (store, forceLoad = false) {
@@ -23,3 +24,14 @@ export function getParty (store, forceLoad = false) {
forceLoad,
});
}
export async function lookingForParty (store, payload) {
let response;
if (payload && payload.page) {
response = await axios.get(`api/v4/looking-for-party?page=${payload.page}`);
} else {
response = await axios.get('api/v4/looking-for-party');
}
return response.data.data;
}
+1
View File
@@ -27,6 +27,7 @@ const envVars = [
'APPLE_AUTH_CLIENT_ID',
'AMPLITUDE_KEY',
'LOGGLY_CLIENT_TOKEN',
'TRUSTED_DOMAINS',
// TODO necessary? if yes how not to mess up with vue cli? 'NODE_ENV'
];
+1 -1
View File
@@ -312,7 +312,7 @@
"teamBasedTasksList": "Team-Based Task List",
"teamBasedTasksListDesc": "Set up an easily-viewed shared task list for the group. Assign tasks to your fellow group members, or let them claim their own tasks to make it clear what everyone is working on!",
"groupManagementControls": "Group Management Controls",
"groupManagementControlsDesc": "Use task approvals to verify that a task that was really completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"groupManagementControlsDesc": "View task status to verify that a task that was completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"inGameBenefits": "In-Game Benefits",
"inGameBenefitsDesc": "Group members get an exclusive Jackalope Mount, as well as full subscription benefits, including special monthly equipment sets and the ability to buy gems with gold.",
"inspireYourParty": "Inspire your party, gamify life together.",
+1 -1
View File
@@ -312,7 +312,7 @@
"teamBasedTasksList": "Team-Based Task List",
"teamBasedTasksListDesc": "Set up an easily-viewed shared task list for the group. Assign tasks to your fellow group members, or let them claim their own tasks to make it clear what everyone is working on!",
"groupManagementControls": "Group Management Controls",
"groupManagementControlsDesc": "Use task approvals to verify that a task that was really completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"groupManagementControlsDesc": "View task status to verify that a task that was completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"inGameBenefits": "In-Game Benefits",
"inGameBenefitsDesc": "Group members get an exclusive Jackalope Mount, as well as full subscription benefits, including special monthly equipment sets and the ability to buy gems with gold.",
"inspireYourParty": "Inspire your party, gamify life together.",
+4 -1
View File
@@ -144,5 +144,8 @@
"achievementPolarProModalText": "لقد جمعت كل الحيوانات الأليفة القطبية!",
"achievementBoneToPick": "جامع العظام",
"achievementBoneToPickText": "فقست جميع الحيوانات الأليفة الهيكلية المغامرة والكلاسيكية!",
"achievementBoneToPickModalText": "لقد جمعت كل الحيوانات الأليفة الهيكلية المغامرة والكلاسيكية!"
"achievementBoneToPickModalText": "لقد جمعت كل الحيوانات الأليفة الهيكلية المغامرة والكلاسيكية!",
"achievementPlantParent": "والد النبات",
"achievementPlantParentText": "فقست جميع الحيوانات الأليفة النباتية في الألوان القياسية: الصبار والتريلنج!",
"achievementPlantParentModalText": "لقد جمعت كل الحيوانات الأليفة النباتية!"
}
+15 -1
View File
@@ -648,5 +648,19 @@
"backgroundHauntedPhotoText": "صورة مسكونة",
"backgroundHauntedPhotoNotes": "تجد نفسك محاصرًا في عالم أحادي اللون لصورة مسكونة.",
"backgroundUndeadHandsText": "أيدي الزومبي",
"backgroundUndeadHandsNotes": "حاول الهروب من براثن أيدي الزومبي."
"backgroundUndeadHandsNotes": "حاول الهروب من براثن أيدي الزومبي.",
"backgrounds022022": "المجموعة 93: صدرت في فبراير 2022",
"backgroundFrozenPolarWatersText": "المياه القطبية المجمدة",
"backgroundFrozenPolarWatersNotes": "استكشف المياه القطبية المجمدة.",
"backgroundWinterCanyonText": "وادي شتوي",
"backgroundWinterCanyonNotes": "اذهب في مغامرة في وادي شتوي!",
"backgroundIcePalaceText": "قصر الجليد",
"backgrounds012022": "المجموعة 92: صدرت في يناير 2022",
"backgroundSnowyFarmText": "مزرعة ثلجية",
"backgroundMeteorShowerText": "دش النيزك",
"backgroundMeteorShowerNotes": "شاهد العرض الليلي المبهر لدش النيزك.",
"backgroundPalmTreeWithFairyLightsText": "شجرة نخيل مع أضواء زخرفية",
"backgroundSnowyFarmNotes": "تأكد من أن الجميع آمنون ودافئون في مزرعتك الثلجية.",
"backgroundIcePalaceNotes": "احكم في قصر الجليد.",
"backgroundPalmTreeWithFairyLightsNotes": "قف بجانب شجرة نخيل ملفوفة بأضواء زخرفية."
}
@@ -1,5 +1,4 @@
{
"tavernCommunityGuidelinesPlaceholder": "Friendly reminder: this is an all-ages chat, so please keep content and language appropriate! Consult the Community Guidelines in the sidebar if you have questions.",
"lastUpdated": "آخر تحديث:",
"commGuideHeadingWelcome": "أهلاً وسهلاً بك في Habitica!",
@@ -7,7 +6,7 @@
"commGuidePara002": "To help keep everyone safe, happy, and productive in the community, we do have some guidelines. We have carefully crafted them to make them as friendly and easy-to-read as possible. Please take the time to read them before you start chatting.",
"commGuidePara003": "تنطبق هذه القواعد على جميع المساحات الإجتماعية التي نستخدمها، بما في ذلك (ولكن لا تقتصر على) Trello، GitHub، Transifex و Wikia (أي الwiki). في بعض الأحيان، سوف تنشأ حالات غير متوقعة، مثل مصدراً جديداً من الصراع أو ظهور مستحضراً للأرواح الشرية. عندما يحدث ذلك، يجوز للمشرفين تعديل هذه المبادئ التوجيهية للحفاظ على سلامة المجتمع من التهديدات الجديدة. ولكن لا تخف: سيتم إعلامك عن طريق إعلان من الآنسة Bailey إذا تغيرت المبادئ التوجيهية.",
"commGuideHeadingInteractions": "التفاعل في Habitica",
"commGuidePara015": "Habitica has two kinds of social spaces: public, and private. Public spaces include the Tavern, Public Guilds, GitHub, Trello, and the Wiki. Private spaces are Private Guilds, Party chat, and Private Messages. All Display Names must comply with the public space guidelines. To change your Display Name, go on the website to User > Profile and click on the \"Edit\" button.",
"commGuidePara015": "يحتوي Habitica على نوعين من الفضاءات الاجتماعية: العامة والخاصة. تشمل الفضاءات العامة التي يمكن الوصول إليها الحانوت والمنتديات العامة و GitHub و Trello والويكي. الفضاءات الخاصة هي الأندية الخاصة والدردشة الجماعية والرسائل الخاصة. يجب على جميع أسماء العرض وأسماء المستخدمين @ اتباع إرشادات الفضاء العام. لتغيير اسم العرض و / أو اسم المستخدم @ ، انتقل إلى القائمة> الإعدادات> الملف الشخصي على الهاتف المحمول. على الويب ، انتقل إلى المستخدم> الإعدادات.",
"commGuidePara016": "عند التنقل ما بين الأماكن العامة في Habitica، هناك بعض القواعد العامة للحفاظ على سعادة وسلامة الجميع. ستكون هذه القواعد سهلة عليك أيها المغامر!",
"commGuideList02A": "<strong>Respect each other</strong>. Be courteous, kind, friendly, and helpful. Remember: Habiticans come from all backgrounds and have had wildly divergent experiences. This is part of what makes Habitica so cool! Building a community means respecting and celebrating our differences as well as our similarities. Here are some easy ways to respect each other:",
"commGuideList02B": "<strong>التزم بجميع <a href='/static/terms' target='_blank'>الشروط والأحكام</a></strong>.",
+2 -2
View File
@@ -1,11 +1,11 @@
{
"frequentlyAskedQuestions": "الأسئلة الأكثر تكراراً",
"faqQuestion0": "أنا محتار. من أين استطيع أن احصل على نظرة عامة؟",
"iosFaqAnswer0": "First, you'll set up tasks that you want to do in your everyday life. Then, as you complete the tasks in real life and check them off, you'll earn experience and gold. Gold is used to buy equipment and some items, as well as custom rewards. Experience causes your character to level up and unlock content such as Pets, Skills, and Quests! You can customize your character under Menu > Customize Avatar.\n\n Some basic ways to interact: click the (+) in the upper-right-hand corner to add a new task. Tap on an existing task to edit it, and swipe left on a task to delete it. You can sort tasks using Tags in the upper-left-hand corner, and expand and contract checklists by clicking on the checklist bubble.",
"iosFaqAnswer0": "أولاً ، ستقوم بإعداد المهام التي تريد القيام بها في حياتك اليومية. ثم ، بمجرد الانتهاء من المهام في الحياة الحقيقية وتحققها ، ستكسب الخبرة والذهب. يتم استخدام الذهب لشراء المعدات وبعض العناصر ، بالإضافة إلى المكافآت المخصصة. يؤدي الخبرة إلى صعود شخصيتك وفتح محتوى مثل الحيوانات الأليفة والمهارات والمهام! يمكنك تخصيص شخصيتك في القائمة> تخصيص الصورة الرمزية.\n\n بعض الطرق الأساسية للتفاعل: انقر على (+) في الزاوية العلوية اليمنى لإضافة مهمة جديدة. انقر فوق المهمة الموجودة لتحريرها ، واسحب إلى اليسار على المهمة لحذفها. يمكنك فرز المهام باستخدام العلامات في الزاوية العلوية اليسرى ، وتوسيع وطي قوائم الاختيار عن طريق النقر على فقاعة قائمة الاختيارات.",
"androidFaqAnswer0": "First, you'll set up tasks that you want to do in your everyday life. Then, as you complete the tasks in real life and check them off, you'll earn experience and gold. Gold is used to buy equipment and some items, as well as custom rewards. Experience causes your character to level up and unlock content such as Pets, Skills, and Quests! You can customize your character under Menu > [Inventory >] Avatar.\n\n Some basic ways to interact: click the (+) in the lower-right-hand corner to add a new task. Tap on an existing task to edit it, and swipe left on a task to delete it. You can sort tasks using Tags in the upper-right-hand corner, and expand and contract checklists by clicking on the checklist count box.",
"webFaqAnswer0": "First, you'll set up tasks that you want to do in your everyday life. Then, as you complete the tasks in real life and check them off, you'll earn Experience and Gold. Gold is used to buy equipment and some items, as well as custom rewards. Experience causes your character to level up and unlock content such as pets, skills, and quests! For more detail, check out a step-by-step overview of the game at [Help -> Overview for New Users](https://habitica.com/static/overview).",
"faqQuestion1": "كيف أضيف مهماتي؟",
"iosFaqAnswer1": "Good Habits (the ones with a +) are tasks that you can do many times a day, such as eating vegetables. Bad Habits (the ones with a -) are tasks that you should avoid, like biting nails. Habits with a + and a - have a good choice and a bad choice, like taking the stairs vs. taking the elevator. Good Habits award experience and gold. Bad Habits subtract health.\n\n Dailies are tasks that you have to do every day, like brushing your teeth or checking your email. You can adjust the days that a Daily is due by tapping to edit it. If you skip a Daily that is due, your avatar will take damage overnight. Be careful not to add too many Dailies at once!\n\n To-Dos are your To-Do list. Completing a To-Do earns you gold and experience. You never lose health from To-Dos. You can add a due date to a To-Do by tapping to edit.",
"iosFaqAnswer1": "العادات الجيدة (تلك التي تحتوي على علامة \"+\") هي المهام التي يمكنك القيام بها عدة مرات في اليوم، مثل تناول الخضروات. العادات السيئة (تلك التي تحتوي على علامة \"-\") هي المهام التي يجب عليك تجنبها، مثل عض الأظافر. والعادات التي تحتوي على علامة \"+\" و \"-\" لديها خيار جيد وخيار سيئ، مثل استخدام الدرج بدلاً من المصعد.\n\nالعادات الجيدة تمنحك خبرة وذهب. والعادات السيئة تقلل من صحتك.\n\nالمهام اليومية هي المهام التي يجب عليك القيام بها كل يوم، مثل تنظيف أسنانك أو التحقق من بريدك الإلكتروني. يمكنك تعديل تواريخ المهام اليومية بالنقر لتحريرها. إذا تخطيت مهمة يومية محددة، فسيتلقى أفاتارك ضررًا خلال الليل. كن حذرًا من عدم إضافة العديد من المهام اليومية في وقت واحد!\n\nالمهام التي يجب القيام بها هي قائمة المهام الخاصة بك. إكمال المهام يمنحك الذهب والخبرة. لن تفقد أبدًا صحتك بسبب هذه المهام. يمكنك إضافة تاريخ استحقاق للمهام التي يجب القيام بها بالنقر لتحريرها.",
"androidFaqAnswer1": "Good Habits (the ones with a +) are tasks that you can do many times a day, such as eating vegetables. Bad Habits (the ones with a -) are tasks that you should avoid, like biting nails. Habits with a + and a - have a good choice and a bad choice, like taking the stairs vs. taking the elevator. Good Habits award experience and gold. Bad Habits subtract health.\n\n Dailies are tasks that you have to do every day, like brushing your teeth or checking your email. You can adjust the days that a Daily is due by tapping to edit it. If you skip a Daily that is due, your character will take damage overnight. Be careful not to add too many Dailies at once!\n\n To-Dos are your To-Do list. Completing a To-Do earns you gold and experience. You never lose health from To-Dos. You can add a due date to a To-Do by tapping to edit.",
"webFaqAnswer1": "* Good Habits (the ones with a :heavy_plus_sign:) are tasks that you can do many times a day, such as eating vegetables. Bad Habits (the ones with a :heavy_minus_sign:) are tasks that you should avoid, like biting nails. Habits with a :heavy_plus_sign: and a :heavy_minus_sign: have a good choice and a bad choice, like taking the stairs vs. taking the elevator. Good Habits award Experience and Gold. Bad Habits subtract Health.\n* Dailies are tasks that you have to do every day, like brushing your teeth or checking your email. You can adjust the days that a Daily is due by clicking the pencil item to edit it. If you skip a Daily that is due, your avatar will take damage overnight. Be careful not to add too many Dailies at once!\n* To-Dos are your To-Do list. Completing a To-Do earns you Gold and Experience. You never lose Health from To-Dos. You can add a due date to a To-Do by clicking the pencil icon to edit.",
"faqQuestion2": "ما هي أمثلة المهمات؟",
+1 -1
View File
@@ -312,7 +312,7 @@
"teamBasedTasksList": "Team-Based Task List",
"teamBasedTasksListDesc": "Set up an easily-viewed shared task list for the group. Assign tasks to your fellow group members, or let them claim their own tasks to make it clear what everyone is working on!",
"groupManagementControls": "Group Management Controls",
"groupManagementControlsDesc": "Use task approvals to verify that a task that was really completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"groupManagementControlsDesc": "View task status to verify that a task that was completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"inGameBenefits": "In-Game Benefits",
"inGameBenefitsDesc": "Group members get an exclusive Jackalope Mount, as well as full subscription benefits, including special monthly equipment sets and the ability to buy gems with gold.",
"inspireYourParty": "Inspire your party, gamify life together.",
+3 -3
View File
@@ -1,11 +1,11 @@
{
"questEvilSantaText": "سانتا الموقع بالفخ",
"questEvilSantaNotes": "You hear agonized roars deep in the icefields. You follow the growls - punctuated by the sound of cackling - to a clearing in the woods, where you see a fully-grown polar bear. She's caged and shackled, fighting for her life. Dancing atop the cage is a malicious little imp wearing a castaway costume. Vanquish Trapper Santa, and save the beast!",
"questEvilSantaNotes": "تسمع زئيرًا متألمة في عمق حقول الجليد. تتبع النموات - المفصولة بصوت الضحك - إلى مساحة صافية في الغابة ، حيث ترى دبًا قطبيًا ناضجًا تمامًا. إنها مسجونة ومقيدة ، تحارب من أجل حياتها. يرقص فوق القفص عفريت صغيرة شريرة ترتدي زي الناجي. اهزم صياد سانتا وانقذ الوحش! <br> <br> <strong> ملاحظة </strong>: \"صياد سانتا\" يمنح إنجازًا قابلًا للتكديس للمهمة ولكنه يعطي جبلًا نادرًا يمكن إضافته إلى طاولة الخيل الخاصة بك مرة واحدة.",
"questEvilSantaCompletion": "Trapper Santa squeals in anger, and bounces off into the night. The grateful she-bear, through roars and growls, tries to tell you something. You take her back to the stables, where Matt Boch the Beast Master listens to her tale with a gasp of horror. She has a cub! He ran off into the icefields when mama bear was captured.",
"questEvilSantaBoss": "سانتا الموقع بالفخ",
"questEvilSantaDropBearCubPolarMount": "دب قطبي (مركب)",
"questEvilSanta2Text": "البحث عن الشبل",
"questEvilSanta2Notes": "When Trapper Santa captured the polar bear mount, her cub ran off into the icefields. You hear twig-snaps and snow crunch through the crystalline sound of the forest. Paw prints! You start racing to follow the trail. Find all the prints and broken twigs, and retrieve the cub!",
"questEvilSanta2Text": "ابحث عن الشبل",
"questEvilSanta2Notes": "عندما اعتقل صياد السانتا الدب القطبي، هرب صغيرها إلى حقول الجليد. تسمع صوت تكسر أغصان الشجر وصوت الثلج يتكسر بينما تجوب الغابة. آثار بصمات الكفوف! تبدأ بالجري لمتابعة الأثر. ابحث عن جميع الأثر والأغصان المكسورة واسترد الصغير!<br><br><strong>ملاحظة</strong>: تمنح \"البحث عن الصغير\" إنجازًا لمهمة يمكن تراكمه ولكن يمنح حيوانًا أليفًا نادرًا يمكن إضافته إلى مستودعك مرة واحدة فقط.",
"questEvilSanta2Completion": "You've found the cub! It will keep you company forever.",
"questEvilSanta2CollectTracks": "مسارات",
"questEvilSanta2CollectBranches": "أغصان مكسورة",
+4 -1
View File
@@ -136,5 +136,8 @@
"cancelSubInfoGoogle": "الرجاء الانتقال إلى \"الحساب\"> قسم \"الاشتراكات\" في متجر Google Play لإلغاء اشتراكك أو لمعرفة تاريخ انتهاء إشتراكك إذا كنت قد ألغيته بالفعل. هذه الشاشة غير قادرة على إظهار ما إذا كان قد تم إلغاء اشتراكك.",
"organization": "منظمة",
"giftASubscription": "إهداء إشتراك",
"viewSubscriptions": "عرض الإشتراكات"
"viewSubscriptions": "عرض الإشتراكات",
"howManyGemsSend": "كم عدد الجواهر التي ترغب في إرسالها؟",
"howManyGemsPurchase": "كم عدد الأحجار الكريمة التي ترغب في شرائها؟",
"needToPurchaseGems": "هل تحتاج إلى شراء جواهر كهدية؟"
}
+6 -6
View File
@@ -1,8 +1,8 @@
{
"achievement": "Achievement",
"onwards": "Onwards!",
"levelup": "By accomplishing your real life goals, you leveled up and are now fully healed!",
"reachedLevel": "You Reached Level <%= level %>",
"achievementLostMasterclasser": "Quest Completionist: Masterclasser Series",
"achievementLostMasterclasserText": "Completed all sixteen quests in the Masterclasser Quest Series and solved the mystery of the Lost Masterclasser!"
"achievement": "Дасягненьне",
"onwards": "Наперад!",
"levelup": "By accomplishing your real life goals, you leveled up and are now fully healed!",
"reachedLevel": "You Reached Level <%= level %>",
"achievementLostMasterclasser": "Quest Completionist: Masterclasser Series",
"achievementLostMasterclasserText": "Completed all sixteen quests in the Masterclasser Quest Series and solved the mystery of the Lost Masterclasser!"
}
+1 -1
View File
@@ -312,7 +312,7 @@
"teamBasedTasksList": "Team-Based Task List",
"teamBasedTasksListDesc": "Set up an easily-viewed shared task list for the group. Assign tasks to your fellow group members, or let them claim their own tasks to make it clear what everyone is working on!",
"groupManagementControls": "Group Management Controls",
"groupManagementControlsDesc": "Use task approvals to verify that a task that was really completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"groupManagementControlsDesc": "View task status to verify that a task that was completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"inGameBenefits": "In-Game Benefits",
"inGameBenefitsDesc": "Group members get an exclusive Jackalope Mount, as well as full subscription benefits, including special monthly equipment sets and the ability to buy gems with gold.",
"inspireYourParty": "Inspire your party, gamify life together.",
+1 -1
View File
@@ -312,7 +312,7 @@
"teamBasedTasksList": "Team-Based Task List",
"teamBasedTasksListDesc": "Set up an easily-viewed shared task list for the group. Assign tasks to your fellow group members, or let them claim their own tasks to make it clear what everyone is working on!",
"groupManagementControls": "Group Management Controls",
"groupManagementControlsDesc": "Use task approvals to verify that a task that was really completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"groupManagementControlsDesc": "View task status to verify that a task that was completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"inGameBenefits": "In-Game Benefits",
"inGameBenefitsDesc": "Group members get an exclusive Jackalope Mount, as well as full subscription benefits, including special monthly equipment sets and the ability to buy gems with gold.",
"inspireYourParty": "Inspire your party, gamify life together.",
+1 -1
View File
@@ -312,7 +312,7 @@
"teamBasedTasksList": "Team-Based Task List",
"teamBasedTasksListDesc": "Set up an easily-viewed shared task list for the group. Assign tasks to your fellow group members, or let them claim their own tasks to make it clear what everyone is working on!",
"groupManagementControls": "Group Management Controls",
"groupManagementControlsDesc": "Use task approvals to verify that a task that was really completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"groupManagementControlsDesc": "View task status to verify that a task that was completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"inGameBenefits": "In-Game Benefits",
"inGameBenefitsDesc": "Group members get an exclusive Jackalope Mount, as well as full subscription benefits, including special monthly equipment sets and the ability to buy gems with gold.",
"inspireYourParty": "Inspire your party, gamify life together.",
+1 -1
View File
@@ -312,7 +312,7 @@
"teamBasedTasksList": "Team-Based Task List",
"teamBasedTasksListDesc": "Set up an easily-viewed shared task list for the group. Assign tasks to your fellow group members, or let them claim their own tasks to make it clear what everyone is working on!",
"groupManagementControls": "Group Management Controls",
"groupManagementControlsDesc": "Use task approvals to verify that a task that was really completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"groupManagementControlsDesc": "View task status to verify that a task that was completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"inGameBenefits": "In-Game Benefits",
"inGameBenefitsDesc": "Group members get an exclusive Jackalope Mount, as well as full subscription benefits, including special monthly equipment sets and the ability to buy gems with gold.",
"inspireYourParty": "Inspire your party, gamify life together.",
+19 -1
View File
@@ -340,5 +340,23 @@
"singleCompletion": "Single - Completes when any assigned user finishes",
"allAssignedCompletion": "All - Completes when all assigned users finish",
"pmReported": "Tak, fordi du rapporterede denne besked.",
"features": "Funktioner"
"features": "Funktioner",
"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",
"selectGift": "Vælg Gave",
"sendGiftToWhom": "Hvem vil du gerne sende en gave til?",
"PMDisabled": "Slå Private Beskeder fra",
"editGuild": "Rediger Klan",
"sendGiftTotal": "I alt:",
"usernameOrUserId": "Indtast @brugernavn eller Bruger ID",
"sendGiftLabel": "Vil du gerne sende en gavebesked?",
"giftMessageTooLong": "Maximum længe for gavebeskeden er <%= maxGiftMessageLength %>.",
"userWithUsernameOrUserIdNotFound": "Brugernavn eller Bruger ID findes ikke.",
"editParty": "Rediger Hold",
"leaveGuild": "Forlad Klan",
"joinGuild": "Tilslut Klan",
"PMUnblockUserToSendMessages": "Fjern denne brugers blokering for at sende og modtage beskeder.",
"selectSubscription": "Vælg Abonnement"
}
+4 -1
View File
@@ -141,5 +141,8 @@
"achievementWoodlandWizardText": "Du hast alle Standard-Farben der Waldkreaturen ausgebrütet: Dachs, Bär, Hirsch, Fuchs, Frosch, Igel, Eule, Schnecke, Eichhörnchen und Bäumling!",
"achievementBoneToPickModalText": "Du hast alle klassischen und Quest-Skeletthaustiere gesammelt!",
"achievementBoneToPick": "Ein harter Knochen",
"achievementBoneToPickText": "Hat alle klassischen und Quest-Skeletthaustiere ausgebrütet!"
"achievementBoneToPickText": "Hat alle klassischen und Quest-Skeletthaustiere ausgebrütet!",
"achievementPolarProText": "Hat alle Standardfarben der Polar-Haustiere ausgebrütet: Bär, Fuchs, Pinguin, Wal und Wolf!",
"achievementPolarPro": "Polar-Profi",
"achievementPolarProModalText": "Du hast alle Polar-Haustiere gesammelt!"
}
+12 -1
View File
@@ -745,5 +745,16 @@
"backgroundAutumnBridgeNotes": "Bewundere die Schönheit einer Brücke im Herbst.",
"backgrounds122022": "Set 103: Veröffentlicht im Dezember 2022",
"backgroundBranchesOfAHolidayTreeText": "Äste eines Festtagsbaums",
"backgroundBranchesOfAHolidayTreeNotes": "Baumle auf den Ästen eines Festtagsbaums."
"backgroundBranchesOfAHolidayTreeNotes": "Baumle auf den Ästen eines Festtagsbaums.",
"backgrounds032023": "SET 106: Veröffentlicht im März 2023",
"backgrounds022023": "SET 195: Veröffentlicht im Februar 2023",
"backgroundGoldenBirdcageText": "Goldener Vogelkäfig",
"backgroundGoldenBirdcageNotes": "Verstecken in einem goldenen Vogelkäfig.",
"backgrounds012023": "SET 104: Veröffentlicht Januar 2023",
"backgroundRimeIceText": "Raureif",
"backgroundRimeIceNotes": "Bewundere funkelnden Raureif.",
"backgroundSnowyTempleText": "Verschneiter Tempel",
"backgroundSnowyTempleNotes": "Einen ruhigen verschneiten Tempel anschauen.",
"backgroundSnowyVillageText": "Verschneites Dorf",
"backgroundSnowyVillageNotes": "Ein verschneites Dorf bewundern."
}
@@ -129,5 +129,6 @@
"commGuidePara017": "Hier ist die Kurzfassung, aber wir möchten Dich ermutigen, weiter unten mehr Details zu erfahren:",
"commGuideList02M": "Frage nicht nach oder bettle nicht um Edelsteine, Abonnements oder die Mitgliedschaft in Gruppenplänen. Nachrichten dieser Art sind weder in der Taverne, noch in öffentlichen oder privaten Chaträumen, und auch nicht in Privatnachrichten erlaubt. Wenn Du Nachrichten erhältst, in denen man Dich um bezahlte Artikel fragt, melde sie bitte über das Fähnchen. Wiederholte oder extreme Betteleien nach Edelsteinen oder Abonnements, vor allem nachdem bereits eine Warnung ausgesprochen wurde, können zu einer Kontosperre führen.",
"commGuideList09D": "Entfernung oder Herabstufung des Mitwirkenden-Ranges",
"commGuideList05H": "Schwerwiegende oder wiederholte Versuche, andere Spielende zu betrügen oder zu bedrängen, um an Gegenstände zu kommen, die echtes Geld kosten"
"commGuideList05H": "Schwerwiegende oder wiederholte Versuche, andere Spielende zu betrügen oder zu bedrängen, um an Gegenstände zu kommen, die echtes Geld kosten",
"commGuideList02N": "<strong>Markiere und melde Nachrichten, in denen diese Richtlinien oder die Nutzungsbedingungen nicht eingehalten werden.</strong> Wir werden uns so schnell wie möglich darum kümmern. Alternativ kannst du Mitarbeiter:innen über <a href='mailto:admin@habitica.com' target='_blank'>admin@habitica.com</a> benachrichtigen, doch die Markierung ist der schnellste Weg, um Hilfe zu erhalten."
}
+1 -1
View File
@@ -166,7 +166,7 @@
"assignedToUser": "Zugewiesen: <strong>@<%- userName %></strong>",
"assignedToMembers": "<%= userCount %> Mitgliedern",
"assignedToYouAndMembers": "<strong>Dir</strong>, <%= userCount %> Mitgliedern",
"youAreAssigned": "Dir zugewiesen",
"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?",
+1 -1
View File
@@ -312,7 +312,7 @@
"teamBasedTasksList": "Team-Based Task List",
"teamBasedTasksListDesc": "Set up an easily-viewed shared task list for the group. Assign tasks to your fellow group members, or let them claim their own tasks to make it clear what everyone is working on!",
"groupManagementControls": "Group Management Controls",
"groupManagementControlsDesc": "Use task approvals to verify that a task that was really completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"groupManagementControlsDesc": "View task status to verify that a task that was completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"inGameBenefits": "In-Game Benefits",
"inGameBenefitsDesc": "Group members get an exclusive Jackalope Mount, as well as full subscription benefits, including special monthly equipment sets and the ability to buy gems with gold.",
"inspireYourParty": "Inspire your party, gamify life together.",
@@ -867,6 +867,22 @@
"backgroundMangroveForestText": "Mangrove Forest",
"backgroundMangroveForestNotes": "Explore the edge of the Mangrove Forest.",
"backgrounds042023": "SET 107: Released April 2023",
"backgroundLeafyTreeTunnelText": "Leafy Tree Tunnel",
"backgroundLeafyTreeTunnelNotes": "Wander through a Leafy Tree Tunnel.",
"backgroundSpringtimeShowerText": "Springtime Shower",
"backgroundSpringtimeShowerNotes": "See a Flowery Springtime Shower.",
"backgroundUnderWisteriaText": "Under Wisteria",
"backgroundUnderWisteriaNotes": "Relax Under Wisteria.",
"backgrounds052023": "SET 108: Released May 2023",
"backgroundInAPaintingText": "In A Painting",
"backgroundInAPaintingNotes": "Enjoy creative pursuits Inside a Painting.",
"backgroundFlyingOverHedgeMazeText": "Flying Over Hedge Maze",
"backgroundFlyingOverHedgeMazeNotes": "Marvel while Flying over a Hedge Maze.",
"backgroundCretaceousForestText": "Cretaceous Forest",
"backgroundCretaceousForestNotes": "Take in the ancient greenery of a Cretaceous Forest.",
"timeTravelBackgrounds": "Steampunk Backgrounds",
"backgroundAirshipText": "Airship",
"backgroundAirshipNotes": "Become a sky sailor on board your very own Airship.",
+1
View File
@@ -311,6 +311,7 @@
"hatchingPotionVirtualPet": "Virtual Pet",
"hatchingPotionPorcelain": "Porcelain",
"hatchingPotionPinkMarble": "Pink Marble",
"hatchingPotionTeaShop": "Tea Shop",
"hatchingPotionNotes": "Pour this on an egg, and it will hatch as a <%= potText(locale) %> pet.",
"premiumPotionAddlNotes": "Not usable on quest pet eggs. Available for purchase until <%= date(locale) %>.",
+64 -6
View File
@@ -1,5 +1,6 @@
{
"frequentlyAskedQuestions": "Frequently Asked Questions",
"general": "General",
"faqQuestion0": "I'm confused. Where do I get an overview?",
"iosFaqAnswer0": "First, you'll set up tasks that you want to do in your everyday life. Then, as you complete the tasks in real life and check them off, you'll earn experience and gold. Gold is used to buy equipment and some items, as well as custom rewards. Experience causes your character to level up and unlock content such as Pets, Skills, and Quests! You can customize your character under Menu > Customize Avatar.\n\n Some basic ways to interact: click the (+) in the upper-right-hand corner to add a new task. Tap on an existing task to edit it, and swipe left on a task to delete it. You can sort tasks using Tags in the upper-left-hand corner, and expand and contract checklists by clicking on the checklist bubble.",
@@ -26,10 +27,10 @@
"androidFaqAnswer4": "There are several things that can cause you to take damage. First, if you left Dailies incomplete overnight and didn't check them off in the screen that popped up the next morning, those unfinished Dailies will damage you. Second, if you tap a bad Habit, it will damage you. Finally, if you are in a Boss Battle with your Party and one of your Party mates did not complete all their Dailies, the Boss will attack you.\n\n The main way to heal is to gain a level, which restores all your health. You can also buy a Health Potion with gold from the Rewards tab on the Tasks page. Plus, at level 10 or above, you can choose to become a Healer, and then you will learn healing skills. If you are in a Party with a Healer, they can heal you as well.",
"webFaqAnswer4": "There are several things that can cause you to take damage. First, if you left Dailies incomplete overnight and didn't check them off in the screen that popped up the next morning, those unfinished Dailies will damage you. Second, if you click a bad Habit, it will damage you. Finally, if you are in a Boss Battle with your party and one of your party mates did not complete all their Dailies, the Boss will attack you. The main way to heal is to gain a level, which restores all your Health. You can also buy a Health Potion with Gold from the Rewards column. Plus, at level 10 or above, you can choose to become a Healer, and then you will learn healing skills. Other Healers can heal you as well if you are in a Party with them. Learn more by clicking \"Party\" in the navigation bar.",
"faqQuestion5": "How do I play Habitica with my friends?",
"faqQuestion5": "Can I play Habitica with others?",
"iosFaqAnswer5": "The best way is to invite them to a Party with you! Parties can go on Quests, battle monsters, and cast skills to support each other.\n\nIf you want to start your own Party, go to Menu > [Party](https://habitica.com/party) and tap \"Create New Party\". Then scroll down and tap \"Invite a Member\" to invite your friends by entering their @username. If you want to join someone elses Party, just give them your @username and they can invite you!\n\nYou and your friends can also join Guilds, which are public chat rooms that bring people together based on shared interests! There are a lot of helpful and fun communities, be sure to check them out.\n\nIf youre feeling more competitive, you and your friends can create or join Challenges to take on a set of tasks. There are all sorts of public Challenges available that span a wide array of interests and goals. Some public Challenges will even award Gem prizes if youre selected as the winner.",
"androidFaqAnswer5": "The best way is to invite them to a Party with you! Parties can go on quests, battle monsters, and cast skills to support each other. Go to the [website](https://habitica.com/) to create one if you don't already have a Party. You can also join guilds together (Social > Guilds). Guilds are chat rooms focusing on a shared interest or the pursuit of a common goal, and can be public or private. You can join as many guilds as you'd like, but only one party.\n\n For more detailed info, check out the wiki pages on [Parties](https://habitica.fandom.com/wiki/Party) and [Guilds](https://habitica.fandom.com/wiki/Guilds).",
"webFaqAnswer5": "The best way is to invite them to a Party with you by clicking \"Party\" in the navigation bar! Parties can go on quests, battle monsters, and cast skills to support each other. You can also join Guilds together (click on \"Guilds\" in the navigation bar). Guilds are chat rooms focusing on a shared interest or the pursuit of a common goal, and can be public or private. You can join as many Guilds as you'd like, but only one Party. For more detailed info, check out the wiki pages on [Parties](https://habitica.fandom.com/wiki/Party) and [Guilds](https://habitica.fandom.com/wiki/Guilds).",
"webFaqAnswer5": "Yes, with Parties! You can start your own Party or join an existing one. Partying with other Habitica players is a great way to take on Quests, receive buffs from Party members skills, and boost your motivation with additional accountability.",
"faqQuestion6": "How do I get a Pet or Mount?",
"iosFaqAnswer6": "Every time you complete a task, you'll have a random chance at receiving an Egg, a Hatching Potion, or a piece of Pet Food. They will be stored in Menu > Items.\n\nTo hatch a Pet, you'll need an Egg and a Hatching Potion. Tap on the Egg to determine the species you want to hatch, and select \"Hatch Egg.\" Then choose a Hatching Potion to determine its color! Go to Menu > Pets and click your new Pet to equip it to your Avatar. \n\n You can also grow your Pets into Mounts by feeding them under Menu > Pets. Tap on a Pet, and select \"Feed Pet\"! You'll have to feed a Pet many times before it becomes a Mount, but if you can figure out its favorite food, it will grow more quickly. Use trial and error, or [see the spoilers here](https://habitica.fandom.com/wiki/Food#Food_Preferences). Once you have a Mount, go to Menu > Mounts and tap on it to equip it to your Avatar.\n\nYou can also get Eggs for Quest Pets by completing certain Quests (to learn more about Quests, see [How do I fight monsters and go on Quests](https://habitica.com/static/faq/9)).",
@@ -46,10 +47,10 @@
"androidFaqAnswer8": "The blue bar that appeared when you hit level 10 and chose a Class is your Mana bar. As you continue to level up, you will unlock special Skills that cost Mana to use. Each Class has different Skills, which appear after level 11 under Menu > Skills. Unlike your health bar, your Mana bar does not reset when you gain a level. Instead, Mana is gained when you complete Good Habits, Dailies, and To Do's, and lost when you indulge bad Habits. You'll also regain some Mana overnight -- the more Dailies you completed, the more you will gain.",
"webFaqAnswer8": "The blue bar that appeared when you hit level 10 and chose a Class is your Mana bar. As you continue to level up, you will unlock special Skills that cost Mana to use. Each Class has different Skills, which appear after level 11 in the action bar at the bottom of the screen. Unlike your Health bar, your Mana bar does not reset when you gain a level. Instead, Mana is gained when you complete good Habits, Dailies, and To Do's, and lost when you indulge bad Habits. You'll also regain some Mana overnight -- the more Dailies you completed, the more you will gain.",
"faqQuestion9": "How do I fight monsters and go on Quests?",
"iosFaqAnswer9": "First, you need to join or start a Party (see [How to play Habitica with my friends](https://habitica.com/static/faq/5)). Although you can battle monsters alone, we recommend playing in a group, because this will make Quests much easier. Plus, having a friend to cheer you on as you accomplish your tasks is very motivating!\n\n Next, you need a Quest Scroll, which are stored under Menu > Items. There are three ways to get a scroll:\n\n - At level 15, you get a Quest-line, aka three linked quests. More Quest-lines unlock at levels 30, 40, and 60 respectively. \n - When you invite people to your Party, you'll be rewarded with the Basi-List Scroll!\n - You can buy Quests from the Quests Shop for Gold and Gems.\n\n To battle the Boss or collect items for a Collection Quest, simply complete your tasks normally, and they will be tallied into damage overnight. (Reloading by pulling down on the screen may be required to see the Boss's health bar go down.) If you are fighting a Boss and you missed any Dailies, the Boss will damage your Party at the same time that you damage the Boss. \n\n After level 11 Mages and Warriors will gain Skills that allow them to deal additional damage to the Boss, so these are excellent classes to choose at level 10 if you want to be a heavy hitter.",
"androidFaqAnswer9": "First, you need to join or start a Party (see above). Although you can battle monsters alone, we recommend playing in a group, because this will make Quests much easier. Plus, having a friend to cheer you on as you accomplish your tasks is very motivating!\n\n Next, you need a Quest Scroll, which are stored under Menu > Items. There are three ways to get a scroll:\n\n - At level 15, you get a Quest-line, aka three linked quests. More Quest-lines unlock at levels 30, 40, and 60 respectively. \n - When you invite people to your Party, you'll be rewarded with the Basi-List Scroll!\n - You can buy Quests from the Quests Shop for Gold and Gems.\n\n To battle the Boss or collect items for a Collection Quest, simply complete your tasks normally, and they will be tallied into damage overnight. (Reloading by pulling down on the screen may be required to see the Boss's health bar go down.) If you are fighting a Boss and you missed any Dailies, the Boss will damage your Party at the same time that you damage the Boss. \n\n After level 11 Mages and Warriors will gain Skills that allow them to deal additional damage to the Boss, so these are excellent classes to choose at level 10 if you want to be a heavy hitter.",
"webFaqAnswer9": "First, you need to join or start a Party by clicking \"Party\" in the navigation bar. Although you can battle monsters alone, we recommend playing in a group, because this will make quests much easier. Plus, having a friend to cheer you on as you accomplish your tasks is very motivating! Next, you need a Quest Scroll, which are stored under Inventory > Quests. There are four ways to get a scroll:\n * When you invite people to your Party, you'll be rewarded with the Basi-List Scroll!\n * At level 15, you get a Quest-line, i.e., three linked quests. More Quest-lines unlock at levels 30, 40, and 60 respectively.\n * You can buy Quests from the Quests Shop (Shops > Quests) for Gold and Gems.\n * When you check in to Habitica a certain number of times, you'll be rewarded with Quest Scrolls. You earn a Scroll during your 1st, 7th, 22nd, and 40th check-ins.\n To battle the Boss or collect items for a Collection Quest, simply complete your tasks normally, and they will be tallied into damage overnight. (Reloading may be required to see the Boss's Health bar go down.) If you are fighting a Boss and you missed any Dailies, the Boss will damage your Party at the same time that you damage the Boss. After level 11 Mages and Warriors will gain Skills that allow them to deal additional damage to the Boss, so these are excellent classes to choose at level 10 if you want to be a heavy hitter.",
"faqQuestion9": "How do I take on Quests?",
"iosFaqAnswer9": "First, you need to join or start a Party by selecting \"Party\" in the navigation. Although you can take on Quests alone, we recommend playing with others to make the Quest faster and boost your motivation with extra accountability.\n\nOnce youre in a Party, you can invite Party members to any Quest scrolls in your Inventory. After everyone accepts, the Quest will begin. To progress, complete your tasks as you normally would! Youll either build up damage against a monster if youre taking on a Boss Quest, or have a chance to find items if youre taking on a Collection Quest. All pending progress is applied the next day.\n\nWhen your Party members do enough damage or collect all items, the Quest ends and everyone will receive their rewards!",
"androidFaqAnswer9": "First, you need to join or start a Party by selecting \"Party\" in the navigation. Although you can take on Quests alone, we recommend playing with others to make the Quest faster and boost your motivation with extra accountability.\n\nOnce youre in a Party, you can invite Party members to any Quest scrolls in your Inventory. After everyone accepts, the Quest will begin. To progress, complete your tasks as you normally would! Youll either build up damage against a monster if youre taking on a Boss Quest, or have a chance to find items if youre taking on a Collection Quest. All pending progress is applied the next day.\n\nWhen your Party members do enough damage or collect all items, the Quest ends and everyone will receive their rewards!",
"webFaqAnswer9": "First, you need to join or start a Party by selecting \"Party\" in the navigation. Although you can take on Quests alone, we recommend playing with others to make the Quest faster and boost your motivation with extra accountability.\n\nOnce youre in a Party, you can invite Party members to any Quest scrolls in your Inventory. After everyone accepts, the Quest will begin. To progress, complete your tasks as you normally would! Youll either build up damage against a monster if youre taking on a Boss Quest, or have a chance to find items if youre taking on a Collection Quest. All pending progress is applied the next day.\n\nWhen your Party members do enough damage or collect all items, the Quest ends and everyone will receive their rewards!",
"faqQuestion10": "What are Gems, and how do I get them?",
"iosFaqAnswer10": "Gems are purchased with real money from Menu > Purchase Gems. When you buy Gems, you are helping us to keep Habitica running. Were very grateful for every bit of support!\n\n In addition to buying Gems directly, there are three other ways players can gain Gems:\n\n * Win a Challenge that has been set up by another player. Go to Menu > Challenges to join some.\n * Subscribe and unlock the ability to buy a certain number of Gems per month.\n * Contribute your skills to the Habitica project. See this wiki page for more details: [Contributing to Habitica](https://habitica.fandom.com/wiki/Contributing_to_Habitica).\n\n Keep in mind that items purchased with Gems do not offer any statistical advantages, so players can still make use of the app without them!",
@@ -71,6 +72,63 @@
"androidFaqAnswer13": "## How do Group Plans work?\n\nA [Group Plan](/group-plans) gives your Party or Guild access to a shared task board thats similar to your personal task board! Its a shared Habitica experience where tasks can be created and checked off by anyone in the group.\n\nThere are also features available like member roles, status view, and task assigning that give you a more controlled experience. [Visit our wiki](https://habitica.fandom.com/wiki/Group_Plans) to learn more about our Group Plans features!\n\n## Who benefits from a Group Plan?\n\nGroup Plans work best when you have a small team of people who want to collaborate together. We recommend 2-5 members.\n\nGroup Plans are great for families, whether its a parent and child or you and a partner. Shared goals, chores, or responsibilities are easy to keep track of on one board.\n\nGroup Plans can also be useful for teams of colleagues that have shared goals, or managers that want to introduce their employees to gamification.\n\n## Quick tips for using Groups\n\nHere are some quick tips to get you started with your new Group. Well provide more details in the following sections:\n\n* Make a member a manager to give them the ability to create and edit tasks\n* Leave tasks unassigned if anyone can complete it and it only needs done once\n* Assign a task to one person to make sure no one else can complete their task\n* Assign a task to multiple people if they all need to complete it\n* Toggle the ability to display shared tasks on your personal board to not miss anything\n* You get rewarded for the tasks you complete, even multi-assigned\n* Task completion rewards arent shared or split between Team members\n* Use task color on the team board to judge the average completion rate of tasks\n* Regularly review the tasks on your Team Board to make sure they are still relevant\n* Missing a Daily wont damage you or your team, but the task will degrade in color\n\n## How can others in the group create tasks?\n\nOnly the group leader and managers can create tasks. If youd like a group member to be able to create tasks, then you should promote them to be a manager by going to the Group Information tab, viewing the member list, and clicking the dot icon by their name.\n\n## How does assigning a task work?\n\nGroup Plans give you the unique ability to assign tasks to other group members. Assigning a task is great for delegating. If you assign a task to someone, then other members are prevented from completing it.\n\nYou can also assign a task to multiple people if it needs to be completed by more than one member. For example, if everyone has to brush their teeth, create a task and assign it to each group member. They will all be able to check it off and get their individual rewards for doing so. The main task will show as complete once everyone checks it off.\n\n## How do unassigned tasks work?\n\nUnassigned tasks can be completed by anyone in the group, so leave a task unassigned to allow any member to complete it. For example, taking out the trash. Whoever takes out the trash can check off the unassigned task and it will show as completed for everyone.\n\n## How does the synchronized day reset work?\n\nShared tasks will reset at the same time for everyone to keep the shared task board in sync. This time is visible on the shared task board and is determined by the group leaders day start time. Because shared tasks reset automatically, you will not get a chance to complete yesterdays uncompleted shared Dailies when you check in the next morning.\n\nShared Dailies will not do damage if they are missed, however they will degrade in color to help visualize progress. We dont want the shared experience to be a negative one!\n\n## How do I use my Group on the mobile apps?\n\nWhile the mobile apps dont fully support all Group Plans functionality yet, you can still complete shared tasks from the iOS and Android app by copying the tasks onto your personal task board. You can switch this preference on from Settings in the mobile apps or from the group task board on the browser version. Now open and assigned shared tasks will display on your personal task board across all platforms.\n\n## Whats the difference between a Groups shared tasks and Challenges?\n\nGroup Plan shared task boards are more dynamic than Challenges, in that they can constantly be updated and interacted with. Challenges are great if you have one set of tasks to send out to many people.\n\nGroup Plans are also a paid feature, while Challenges are available free to everyone.\n\nYou cannot assign specific tasks in Challenges, and Challenges do not have a shared day reset. In general, Challenges offer less control and direct interaction.",
"webFaqAnswer13": "## How do Group Plans work?\n\nA [Group Plan](/group-plans) gives your Party or Guild access to a shared task board thats similar to your personal task board! Its a shared Habitica experience where tasks can be created and checked off by anyone in the group.\n\nThere are also features available like member roles, status view, and task assigning that give you a more controlled experience. [Visit our wiki](https://habitica.fandom.com/wiki/Group_Plans) to learn more about our Group Plans features!\n\n## Who benefits from a Group Plan?\n\nGroup Plans work best when you have a small team of people who want to collaborate together. We recommend 2-5 members.\n\nGroup Plans are great for families, whether its a parent and child or you and a partner. Shared goals, chores, or responsibilities are easy to keep track of on one board.\n\nGroup Plans can also be useful for teams of colleagues that have shared goals, or managers that want to introduce their employees to gamification.\n\n## Quick tips for using Groups\n\nHere are some quick tips to get you started with your new Group. Well provide more details in the following sections:\n\n* Make a member a manager to give them the ability to create and edit tasks\n* Leave tasks unassigned if anyone can complete it and it only needs done once\n* Assign a task to one person to make sure no one else can complete their task\n* Assign a task to multiple people if they all need to complete it\n* Toggle the ability to display shared tasks on your personal board to not miss anything\n* You get rewarded for the tasks you complete, even multi-assigned\n* Task completion rewards arent shared or split between Team members\n* Use task color on the team board to judge the average completion rate of tasks\n* Regularly review the tasks on your Team Board to make sure they are still relevant\n* Missing a Daily wont damage you or your team, but the task will degrade in color\n\n## How can others in the group create tasks?\n\nOnly the group leader and managers can create tasks. If youd like a group member to be able to create tasks, then you should promote them to be a manager by going to the Group Information tab, viewing the member list, and clicking the dot icon by their name.\n\n## How does assigning a task work?\n\nGroup Plans give you the unique ability to assign tasks to other group members. Assigning a task is great for delegating. If you assign a task to someone, then other members are prevented from completing it.\n\nYou can also assign a task to multiple people if it needs to be completed by more than one member. For example, if everyone has to brush their teeth, create a task and assign it to each group member. They will all be able to check it off and get their individual rewards for doing so. The main task will show as complete once everyone checks it off.\n\n## How do unassigned tasks work?\n\nUnassigned tasks can be completed by anyone in the group, so leave a task unassigned to allow any member to complete it. For example, taking out the trash. Whoever takes out the trash can check off the unassigned task and it will show as completed for everyone.\n\n## How does the synchronized day reset work?\n\nShared tasks will reset at the same time for everyone to keep the shared task board in sync. This time is visible on the shared task board and is determined by the group leaders day start time. Because shared tasks reset automatically, you will not get a chance to complete yesterdays uncompleted shared Dailies when you check in the next morning.\n\nShared Dailies will not do damage if they are missed, however they will degrade in color to help visualize progress. We dont want the shared experience to be a negative one!\n\n## How do I use my Group on the mobile apps?\n\nWhile the mobile apps dont fully support all Group Plans functionality yet, you can still complete shared tasks from the iOS and Android app by copying the tasks onto your personal task board. You can switch this preference on from Settings in the mobile apps or from the group task board on the browser version. Now open and assigned shared tasks will display on your personal task board across all platforms.\n\n## Whats the difference between a Groups shared tasks and Challenges?\n\nGroup Plan shared task boards are more dynamic than Challenges, in that they can constantly be updated and interacted with. Challenges are great if you have one set of tasks to send out to many people.\n\nGroup Plans are also a paid feature, while Challenges are available free to everyone.\n\nYou cannot assign specific tasks in Challenges, and Challenges do not have a shared day reset. In general, Challenges offer less control and direct interaction.",
"parties": "Parties",
"faqQuestion14": "How do I find a Party when I'm not in one?",
"iosFaqAnswer14": "If you want to experience Habitica with others but dont know other players, searching for a Party is your best option! If you already know other players that have a Party, you can share your @username with them to be invited. Alternatively, you can create a new Party and invite them with their @username or email address.\n\nTo create or search for a Party, select “Party” in the navigation then choose the option that works for you.",
"androidFaqAnswer14": "If you want to experience Habitica with others but dont know other players, searching for a Party is your best option! If you already know other players that have a Party, you can share your @username with them to be invited. Alternatively, you can create a new Party and invite them with their @username or email address.\n\nTo create or search for a Party, select “Party” in the navigation then choose the option that works for you.",
"webFaqAnswer14": "If you want to experience Habitica with others but dont know other players, searching for a Party is your best option! If you already know other players that have a Party, you can share your @username with them to be invited. Alternatively, you can create a new Party and invite them with their @username or email address.\n\nTo create or search for a Party, select “Party” in the navigation then choose the option that works for you.",
"faqQuestion15": "How does searching for a Party work?",
"iosFaqAnswer15": "After selecting “Look for a Party” in the Party pop-up, youll be added to a list of players that want to join a Party. Party leaders can view this list and send invitations. Once you receive an invitation, you can accept it from your notifications to join the Party of your choosing!\n\nYou may get multiple invitations to different Parties. However, you can only be a member of one Party at a time.",
"androidFaqAnswer15": "After selecting “Look for a Party” in the Party pop-up, youll be added to a list of players that want to join a Party. Party leaders can view this list and send invitations. Once you receive an invitation, you can accept it from your notifications to join the Party of your choosing!\n\nYou may get multiple invitations to different Parties. However, you can only be a member of one Party at a time.",
"webFaqAnswer15": "After selecting “Look for a Party” in the Party pop-up, youll be added to a list of players that want to join a Party. Party leaders can view this list and send invitations. Once you receive an invitation, you can accept it from your notifications to join the Party of your choosing!\n\nYou may get multiple invitations to different Parties. However, you can only be a member of one Party at a time.",
"faqQuestion16": "How long can I search for a Party after joining the list?",
"iosFaqAnswer16": "You will remain in the list until you accept an invite to a Party or dont login for 7 days, whichever comes first.",
"androidFaqAnswer16": "You will remain in the list until you accept an invite to a Party or dont login for 7 days, whichever comes first.",
"webFaqAnswer16": "You will remain in the list until you accept an invite to a Party or dont login for 7 days, whichever comes first.",
"faqQuestion17": "Can I stop searching for a Party?",
"iosFaqAnswer17": "You can leave the list at any time. To stop searching for Party on the website, select the “Party” link in the navigation then click “Leave” in the pop-up.",
"androidFaqAnswer17": "You can leave the list at any time. To stop searching for Party on the website, select the “Party” link in the navigation then click “Leave” in the pop-up.",
"webFaqAnswer17": "You can leave the list at any time. To stop searching for Party on the website, select the “Party” link in the navigation then click “Leave” in the pop-up.",
"faqQuestion18": "I have a Party, how do I find more members?",
"iosFaqAnswer18": "If you are using Habiticas website, select “Find Members” from the Party dropdown. This will display a list of Habitica players that are actively looking for a Party and can be invited to join.\n\nYou'll see some information, such as language, class, level, and how many days they have used Habitica to help find a good fit for your Party. If youd like to chat with someone before sending an invite, you can view their Profile and send a message.",
"androidFaqAnswer18": "If you are using Habiticas website, select “Find Members” from the Party dropdown. This will display a list of Habitica players that are actively looking for a Party and can be invited to join.\n\nYou'll see some information, such as language, class, level, and how many days they have used Habitica to help find a good fit for your Party. If youd like to chat with someone before sending an invite, you can view their Profile and send a message.",
"webFaqAnswer18": "If you are using Habiticas website, select “Find Members” from the Party dropdown. This will display a list of Habitica players that are actively looking for a Party and can be invited to join.\n\nYou'll see some information, such as language, class, level, and how many days they have used Habitica to help find a good fit for your Party. If youd like to chat with someone before sending an invite, you can view their Profile and send a message.",
"faqQuestion19": "How many members can I invite to my Party?",
"iosFaqAnswer19": "Parties have a maximum limit of 30 members and a minimum of 1 member. Pending invites count towards the member count. For example, 29 members and 1 pending invite would count as 30 members. To clear a pending invite, the invited player must accept or decline, or the Party leader must cancel the invite.",
"androidFaqAnswer19": "Parties have a maximum limit of 30 members and a minimum of 1 member. Pending invites count towards the member count. For example, 29 members and 1 pending invite would count as 30 members. To clear a pending invite, the invited player must accept or decline, or the Party leader must cancel the invite.",
"webFaqAnswer19": "Parties have a maximum limit of 30 members and a minimum of 1 member. Pending invites count towards the member count. For example, 29 members and 1 pending invite would count as 30 members. To clear a pending invite, the invited player must accept or decline, or the Party leader must cancel the invite.",
"faqQuestion20": "Can I invite someone I already know?",
"iosFaqAnswer20": "Yes! If you already have a Habitica players username or email address, you can invite them to join your Party. Heres how to send an invite on the different platforms:\n\n## On Habiticas website\n\nNavigate to your Party and click “Invite to Party” on the right-hand side of the page.\n\n## On iOS and Android\n\nTap “Party” from the Menu. Scroll to the Members section and tap “Invite a Member”.",
"androidFaqAnswer20": "Yes! If you already have a Habitica players username or email address, you can invite them to join your Party. Heres how to send an invite on the different platforms:\n\n## On Habiticas website\n\nNavigate to your Party and click “Invite to Party” on the right-hand side of the page.\n\n## On iOS and Android\n\nTap “Party” from the Menu. Scroll to the Members section and tap “Invite a Member”.",
"webFaqAnswer20": "Yes! If you already have a Habitica players username or email address, you can invite them to join your Party. Heres how to send an invite on the different platforms:\n\n## On Habiticas website\n\nNavigate to your Party and click “Invite to Party” on the right-hand side of the page.\n\n## On iOS and Android\n\nTap “Party” from the Menu. Scroll to the Members section and tap “Invite a Member”.",
"faqQuestion21": "How do I cancel a pending invitation to my Party?",
"iosFaqAnswer21": "To cancel a pending invitation on Habiticas website:\n\n1. Click on Member list when viewing your Party\n\n2. Click the “Invites” tab\n\n3. Click the three dots beside the users invite you wish to cancel\n\n4. Choose “Cancel Invite”\n\n\n\nYoull be able to cancel a pending invitation from the mobile apps soon as well!",
"androidFaqAnswer21": "To cancel a pending invitation on Habiticas website:\n\n1. Click on Member list when viewing your Party\n\n2. Click the “Invites” tab\n\n3. Click the three dots beside the users invite you wish to cancel\n\n4. Choose “Cancel Invite”\n\n\n\nYoull be able to cancel a pending invitation from the mobile apps soon as well!",
"webFaqAnswer21": "To cancel a pending invitation on Habiticas website:\n\n1. Click on Member list when viewing your Party\n\n2. Click the “Invites” tab\n\n3. Click the three dots beside the users invite you wish to cancel\n\n4. Choose “Cancel Invite”\n\n\n\nYoull be able to cancel a pending invitation from the mobile apps soon as well!",
"faqQuestion22": "How do I stop unwanted invitations?",
"iosFaqAnswer22": "Once you join a Party youll stop receiving any more invitations. If you want to prevent invites and future communications from a specific player, view their profile and click the Block button.\n\nIf you encounter a situation where you believe another player has broken our Community Guidelines in their name, profile, or a message they sent, please report any messages or reach out to us at admin@habitica.com.",
"androidFaqAnswer22": "Once you join a Party youll stop receiving any more invitations. If you want to prevent invites and future communications from a specific player, view their profile and click the Block button.\n\nIf you encounter a situation where you believe another player has broken our Community Guidelines in their name, profile, or a message they sent, please report any messages or reach out to us at admin@habitica.com.",
"webFaqAnswer22": "Once you join a Party youll stop receiving any more invitations. If you want to prevent invites and future communications from a specific player, view their profile and click the Block button.\n\nIf you encounter a situation where you believe another player has broken our Community Guidelines in their name, profile, or a message they sent, please report any messages or reach out to us at admin@habitica.com.",
"faqQuestion23": "How do I filter the list of members searching for a Party?",
"iosFaqAnswer23": "At the moment there is no way to filter the list of members searching for a Party. However, we have plans to introduce filters in the future, such as class, level, and language.",
"androidFaqAnswer23": "At the moment there is no way to filter the list of members searching for a Party. However, we have plans to introduce filters in the future, such as class, level, and language.",
"webFaqAnswer23": "At the moment there is no way to filter the list of members searching for a Party. However, we have plans to introduce filters in the future, such as class, level, and language.",
"faqQuestion24": "How do I search for a Party on Android or iOS?",
"iosFaqAnswer24": "Were working on adding the ability to search for a Party and find Party members on mobile soon! Keep an eye out for Android version 4.2 and iOS version 3.8.",
"androidFaqAnswer24": "Were working on adding the ability to search for a Party and find Party members on mobile soon! Keep an eye out for Android version 4.2 and iOS version 3.8.",
"webFaqAnswer24": "Were working on adding the ability to search for a Party and find Party members on mobile soon! Keep an eye out for Android version 4.2 and iOS version 3.8.",
"iosFaqStillNeedHelp": "If you have a question that isn't on this list or on the [Wiki FAQ](https://habitica.fandom.com/wiki/FAQ), come ask in the Tavern chat under Menu > Tavern! 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), come ask in the Tavern chat under Menu > Tavern! 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), come ask in the [Habitica Help guild](https://habitica.com/groups/guild/5481ccf3-5d2d-48a9-a871-70a7380cee5a)! We're happy to help."
+2 -2
View File
@@ -65,7 +65,7 @@
"pkQuestion1": "What inspired Habitica? How did it start?",
"pkAnswer1": "If youve ever invested time in leveling up a character in a game, its hard not to wonder how great your life would be if you put all of that effort into improving your real-life self instead of your avatar. We starting building Habitica to address that question. <br /> Habitica officially launched with a Kickstarter in 2013, and the idea really took off. Since then, its grown into a huge project, supported by our awesome open-source volunteers and our generous users.",
"pkQuestion2": "Why does Habitica work?",
"pkAnswer2": "Forming a new habit is hard because people really need that obvious, instant reward. For example, its tough to start flossing, because even though our dentist tells us that it's healthier in the long run, in the immediate moment it just makes your gums hurt. <br /> Habitica's gamification adds a sense of instant gratification to everyday objectives by rewarding a tough task with experience, gold… and maybe even a random prize, like a dragon egg! This helps keep people motivated even when the task itself doesn't have an intrinsic reward, and we've seen people turn their lives around as a result. You can check out success stories here: https://habitversary.tumblr.com",
"pkAnswer2": "Forming a new habit is hard because people really need that obvious, instant reward. For example, its tough to start flossing, because even though our dentist tells us that it's healthier in the long run, in the immediate moment it just makes your gums hurt. <br /> Habitica's gamification adds a sense of instant gratification to everyday objectives by rewarding a tough task with experience, gold… and maybe even a random prize, like a dragon egg! This helps keep people motivated even when the task itself doesn't have an intrinsic reward, and we've seen people turn their lives around as a result.",
"pkQuestion3": "Why did you add social features?",
"pkAnswer3": "Social pressure is a huge motivating factor for a lot of people, so we knew that we wanted to have a strong community that would hold each other accountable for their goals and cheer for their successes. Luckily, one of the things that multiplayer video games do best is foster a sense of community among their users! Habiticas community structure borrows from these types of games; you can form a small Party of close friends, but you can also join a larger, shared-interest groups known as a Guild. Although some users choose to play solo, most decide to form a support network that encourages social accountability through features such as Quests, where Party members pool their productivity to battle monsters together.",
"pkQuestion4": "Why does skipping tasks remove your avatars health?",
@@ -177,7 +177,7 @@
"joinToday": "Join Habitica Today",
"featuredIn": "Featured in",
"signup": "Sign Up",
"getStarted": "Get Started!",
"getStarted": "Get Started",
"mobileApps": "Mobile Apps",
"learnMore": "Learn More",
"translateHabitica": "Translate Habitica"
+53 -1
View File
@@ -468,6 +468,15 @@
"weaponSpecialWinter2023HealerText": "Throwing Wreath",
"weaponSpecialWinter2023HealerNotes": "Watch this festive, prickly wreath spin through the air toward your enemy or obstacles and return to you like a boomerang for another throw. Increases Intelligence by <%= int %>. Limited Edition 2022-2023 Winter Gear.",
"weaponSpecialSpring2023RogueText": "Nibbled Leaf",
"weaponSpecialSpring2023RogueNotes": "Slash! Swat! Snack! Get strong and ready for your coming metamorphosis. Increases Strength by <%= str %>. Limited Edition 2023 Spring Gear.",
"weaponSpecialSpring2023WarriorText": "Hummingbird Foil",
"weaponSpecialSpring2023WarriorNotes": "En garde! Fend off foes from your flowers with this foil! Increases Strength by <%= str %>. Limited Edition 2023 Spring Gear.",
"weaponSpecialSpring2023MageText": "Moonstone Magic",
"weaponSpecialSpring2023MageNotes": "The greater their glow, the more potent is their power. Increases Intelligence by <%= int %>. Limited Edition 2023 Spring Gear.",
"weaponSpecialSpring2023HealerText": "Lilium Pollen",
"weaponSpecialSpring2023HealerNotes": "With a puff and a sparkle, you deploy new growth, joy, and color. Increases Intelligence by <%= int %>. Limited Edition 2023 Spring 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",
@@ -685,6 +694,8 @@
"weaponArmoireMagicSpatulaNotes": "Watch your food fly and flip in the air. You get good luck for the day if it magically flips over three times and then lands back on your spatula. Increases Perception by <%= per %>. Enchanted Armoire: Cooking Implements Set (Item 1 of 2).",
"weaponArmoireFinelyCutGemText": "Finely Cut Gem",
"weaponArmoireFinelyCutGemNotes": "What a find! This stunning, precision-cut gem will be the prize of your collection. And it might contain some special magic, just waiting for you to tap into it. Increases Constitution by <%= con %>. Enchanted Armoire: Jeweler Set (Item 4 of 4).",
"weaponArmoirePaintbrushText": "Paintbrush",
"weaponArmoirePaintbrushNotes": "A jolt of pure inspiration rushes through you when you pick up this brush, allowing you to paint anything you can imagine. Increases Intelligence by <%= int %>. Enchanted Armoire: Painter Set (Item 3 of 4).",
"armor": "armor",
"armorCapitalized": "Armor",
@@ -1131,6 +1142,15 @@
"armorSpecialWinter2023HealerText": "Cardinal Suit",
"armorSpecialWinter2023HealerNotes": "This bright cardinal suit is perfect for flying high above your problems. Increases Constitution by <%= con %>. Limited Edition 2022-2023 Winter Gear.",
"armorSpecialSpring2023RogueText": "Caterpillar Cape",
"armorSpecialSpring2023RogueNotes": "You may only have four limbs to work with, but you can climb and crawl with the greatest of grubs. Increases Perception by <%= per %>. Limited Edition 2023 Spring Gear.",
"armorSpecialSpring2023WarriorText": "Hummingbird Armor",
"armorSpecialSpring2023WarriorNotes": "That humming sound you hear is your wings beating faster than you can imagine. Increases Constitution by <%= con %>. Limited Edition 2023 Spring Gear.",
"armorSpecialSpring2023MageText": "Moonstone Suit",
"armorSpecialSpring2023MageNotes": "This snazzy spring suit magnifies moonstone magic. Increases Intelligence by <%= int %>. Limited Edition 2023 Spring Gear.",
"armorSpecialSpring2023HealerText": "Lily Leaf Gown",
"armorSpecialSpring2023HealerNotes": "A sweep of verdant glory to make you the envy of the Party. Increases Constitution by <%= con %>. Limited Edition 2023 Spring 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",
@@ -1245,6 +1265,9 @@
"armorMystery202210Notes": "Try slithering for a change, you may find it's quite an efficient mode of transportation! Confers no benefit. October 2022 Subscriber Item.",
"armorMystery202212Text": "Glacial Dress",
"armorMystery202212Notes": "The universe can be cold, but this charming dress will keep you cozy as you fly. Confers no benefit. December 2022 Subscriber Item.",
"armorMystery202304Text": "Tiptop Teapot Armor",
"armorMystery202304Notes": "Here is your handle and here is your spout! Confers no benefit. April 2023 Subscriber Item.",
"armorMystery301404Text": "Steampunk Suit",
"armorMystery301404Notes": "Dapper and dashing, wot! Confers no benefit. February 3015 Subscriber Item.",
"armorMystery301703Text": "Steampunk Peacock Gown",
@@ -1432,6 +1455,8 @@
"armorArmoireTeaGownNotes": "Youre resilient, creative, brilliant, and so fashionable! Increases Strength and Intelligence by <%= attrs %> each. Enchanted Armoire: Tea Party Set (Item 1 of 3).",
"armorArmoireBasketballUniformText": "Basketball Uniform",
"armorArmoireBasketballUniformNotes": "Wondering whats printed on the back of this uniform? Its your lucky number, of course! Increases Perception by <% per %>. Enchanted Armoire: Old Timey Basketball Set (Item 1 of 2).",
"armorArmoirePaintersApronText": "Painter's Apron",
"armorArmoirePaintersApronNotes": "This apron can protect your clothes from paint and your creative projects from harsh critiques. Increases Constitution by <%= con %>. Enchanted Armoire: Painter Set (Item 1 of 4).",
"headgear": "helm",
"headgearCapitalized": "Headgear",
@@ -1873,6 +1898,15 @@
"headSpecialWinter2023HealerText": "Cardinal Helm",
"headSpecialWinter2023HealerNotes": "This cardinal helm is perfect for whistling and singing to herald the winter season. Increases Intelligence by <%= int %>. Limited Edition 2022-2023 Winter Gear.",
"headSpecialSpring2023RogueText": "Caterpillar Cowl",
"headSpecialSpring2023RogueNotes": "Be sure to tuck in those tempting antennae when birds are hunting overhead! Increases Perception by <%= per %>. Limited Edition 2023 Spring Gear.",
"headSpecialSpring2023WarriorText": "Hummingbird Helmet",
"headSpecialSpring2023WarriorNotes": "Cover your visage in iridescent feathers when you fly into battle. Increases Strength by <%= str %>. Limited Edition 2023 Spring Gear.",
"headSpecialSpring2023MageText": "Moonstone Visor",
"headSpecialSpring2023MageNotes": "Youll want to wear these glasses at night so you can see clearly by the light of the moon. Increases Perception by <%= per %>. Limited Edition 2023 Spring Gear.",
"headSpecialSpring2023HealerText": "Lily Bloom",
"headSpecialSpring2023HealerNotes": "This brilliant and colorful display shares a color scheme with the Orb of Rebirth! How symbolic! Increases Intelligence by <%= int %>. Limited Edition 2023 Spring 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.",
@@ -2020,6 +2054,9 @@
"headMystery202301Notes": "Your hearing will be so sharp you'll hear the dawn breaking and the dew sparkling. Confers no benefit. January 2023 Subscriber Item.",
"headMystery202303Text": "Mane Character Hair",
"headMystery202303Notes": "What better way to let everyone know youre the star of this tale than to have blue and improbably spiky hair? Confers no benefit. March 2023 Subscriber Item.",
"headMystery202304Text": "Tiptop Teapot Lid",
"headMystery202304Notes": "Wear this helm for your own safe-tea. Confers no benefit. April 2023 Subscriber Item.",
"headMystery301404Text": "Fancy Top Hat",
"headMystery301404Notes": "A fancy top hat for the finest of gentlefolk! January 3015 Subscriber Item. Confers no benefit.",
"headMystery301405Text": "Basic Top Hat",
@@ -2197,6 +2234,10 @@
"headArmoireFancyPirateHatNotes": "Be protected from the sun and any seagulls flying overhead as you drink tea on the deck of your ship. Increases Perception by <%= per %>. Enchanted Armoire: Fancy Pirate Set (Item 2 of 3).",
"headArmoireTeaHatText": "Tea Party Hat",
"headArmoireTeaHatNotes": "This elegant hat is both fancy and functional. Increases Perception by <%= per %>. Enchanted Armoire: Tea Party Set (Item 2 of 3).",
"headArmoireBeaniePropellerHatText": "Beanie Propeller Hat",
"headArmoireBeaniePropellerHatNotes": "This isnt the time to keep your feet on the ground! Spin this little propeller and rise as high as your ambitions will take you. Increases all stats by <%= attrs %>. Enchanted Armoire: Independent Item.",
"headArmoirePaintersBeretText": "Painter's Beret",
"headArmoirePaintersBeretNotes": "See the world with a more artistic eye when you wear this jaunty beret. Increases Perception by <%= per %>. Enchanted Armoire: Painter Set (Item 2 of 4).",
"offhand": "off-hand item",
"offHandCapitalized": "Off-Hand Item",
@@ -2444,6 +2485,11 @@
"shieldSpecialWinter2023HealerText": "Cool Jams",
"shieldSpecialWinter2023HealerNotes": "Your song of frost and snow will soothe the spirits of all who hear. Increases Constitution by <%= con %>. Limited Edition 2022-2023 Winter Gear.",
"shieldSpecialSpring2023WarriorText": "Flower Bunch",
"shieldSpecialSpring2023WarriorNotes": "Collect the springs best flowers into this brightly colored floral bunch. Increases Constitution by <%= con %>. Limited Edition 2023 Spring Gear.",
"shieldSpecialSpring2023HealerText": "Lily Corsage",
"shieldSpecialSpring2023HealerNotes": "An accent for a healing visit, or part of a ritual for attending a springtime dance! Increases Constitution by <%= con %>. Limited Edition 2023 Spring 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",
@@ -2606,7 +2652,9 @@
"shieldArmoireTeaKettleText": "Tea Kettle",
"shieldArmoireTeaKettleNotes": "All your favorite, flavorful teas can be brewed in this kettle. Are you in the mood for black tea, green tea, oolong, or perhaps an herbal infusion? Increases Constitution by <%= con %>. Enchanted Armoire: Tea Party Set (Item 3 of 3).",
"shieldArmoireBasketballText": "Basketball",
"shieldArmoireBasketballNotes": "Swish! Whenever you shoot this magic basketball, there will be nothing but net. Increases Constitution and Strength by <%= attrs %> each. Enchanted Armoire: Old Timey Basketball Set (Item 2 of 2).",
"shieldArmoireBasketballNotes": "Swish! Whenever you shoot this magic basketball, there will be nothing but net. Increases Constitution and Strength by <%= attrs %> each. Enchanted Armoire: Old Timey Basketball Set (Item 2 of 2).",
"shieldArmoirePaintersPaletteText": "Painter's Palette",
"shieldArmoirePaintersPaletteNotes": "Paints in all colors of the rainbow are at your disposal. Is it magic that makes them so vivid when you use them, or is it your talent? Increases Strength by <%= str %>. Enchanted Armoire: Painter Set (Item 4 of 4).",
"back": "Back Accessory",
"backBase0Text": "No Back Accessory",
@@ -2677,6 +2725,8 @@
"backMystery202301Notes": "These fluffy tails contain ethereal power and also a high level of charm! Confers no benefit. January 2023 Subscriber Item.",
"backMystery202302Text": "Trickster Tabby Tail",
"backMystery202302Notes": "Anytime you wear this tail it's sure to be a frabjous day! Callooh! Callay! Confers no benefit. February 2023 Subscriber Item.",
"backMystery202305Text": "Eventide Wings",
"backMystery202305Notes": "Catch the sparkle of the evening star and soar to strange realms on these wings. Confers no benefit. May 2023 Subscriber Item.",
"backSpecialWonderconRedText": "Mighty Cape",
"backSpecialWonderconRedNotes": "Swishes with strength and beauty. Confers no benefit. Special Edition Convention Item.",
@@ -2888,6 +2938,8 @@
"headAccessoryMystery202212Notes": "Magnify your warmth and friendship to new heights with this ornate golden tiara. Confers no benefit. December 2022 Subscriber Item.",
"headAccessoryMystery202302Text": "Trickster Tabby Ears",
"headAccessoryMystery202302Notes": "The purr-fect accessory to set off your enchanting grin. Confers no benefit. February 2023 Subscriber Item.",
"headAccessoryMystery202305Text": "Eventide Horns",
"headAccessoryMystery202305Notes": "These horns glow with reflected moonlight. Confers no benefit. May 2023 Subscriber Item.",
"headAccessoryMystery301405Text": "Headwear Goggles",
"headAccessoryMystery301405Notes": "\"Goggles are for your eyes,\" they said. \"Nobody wants goggles that you can only wear on your head,\" they said. Hah! You sure showed them! Confers no benefit. August 3015 Subscriber Item.",
+5 -1
View File
@@ -212,5 +212,9 @@
"contactForm": "Contact the Moderation Team",
"loadEarlierMessages": "Load Earlier Messages",
"askQuestion": "Ask a Question",
"emptyReportBugMessage": "Report Bug Message missing"
"emptyReportBugMessage": "Report Bug Message missing",
"refreshList": "Refresh List",
"leaveHabitica": "You are about to leave Habitica.com",
"leaveHabiticaText": "Habitica is not responsible for the content of any linked website that is not owned or operated by HabitRPG.<br>Please note that these websites' practices may differ from Habiticas community guidelines.",
"skipExternalLinkModal": "Hold CTRL (Windows) or Command (Mac) when clicking a link to skip this modal."
}
+21 -6
View File
@@ -25,6 +25,7 @@
"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 Guild <span class=\"notification-bold\"><%- guild %></span>",
"invitedToPublicGuild": "You were invited to join the Guild <span class=\"notification-bold-blue\"><%- guild %></span>",
"invitationAcceptedHeader": "Your Invitation has been Accepted",
@@ -137,7 +138,9 @@
"sendGiftSubscription": "<%= months %> Month(s): $<%= price %> USD",
"gemGiftsAreOptional": "Please note that Habitica will never require you to gift gems to other players. Begging people for gems is a <strong>violation of the Community Guidelines</strong>, and all such instances should be reported to <%= hrefTechAssistanceEmail %>.",
"giftMessageTooLong": "The maximum length for gift messages is <%= maxGiftMessageLength %>.",
"battleWithFriends": "Battle Monsters With Friends",
"battleWithFriends": "Play Habitica with Others",
"questWithOthers": "Take on Quests with Others",
"startPartyDetail": "Start your own Party or join an existing one <br/>to take on Quests and boost your motivation!",
"startAParty": "Start a Party",
"partyUpName": "Party Up",
"partyOnName": "Party On",
@@ -173,6 +176,7 @@
"usernamesMustBeAnArray": "Username invites must be an array.",
"canOnlyInviteMaxInvites": "You can only invite \"<%= maxInvites %>\" at a time",
"partyExceedsMembersLimit": "Party size is limited to <%= maxMembersParty %> members",
"partyExceedsInvitesLimit": "A Party may only have up to <%= maxInvites %> pending invitations.",
"onlyCreatorOrAdminCanDeleteChat": "Not authorized to delete this message!",
"onlyGroupLeaderCanEditTasks": "Not authorized to manage tasks!",
"onlyGroupTasksCanBeAssigned": "Only group tasks can be assigned",
@@ -288,17 +292,20 @@
"noGuildsParagraph2": "Click the Discover tab to see recommended Guilds based on your interests, browse Habitica's public Guilds, or create your own Guild.",
"noGuildsMatchFilters": "We couldn't find any matching Guilds.",
"privateDescription": "A private Guild will not be displayed in Habitica's Guild directory. New members can be added by invitation only.",
"removeInvite": "Remove Invitation",
"removeInvite": "Cancel Invite",
"removeMember": "Remove Member",
"sendMessage": "Send Message",
"promoteToLeader": "Transfer Ownership",
"inviteFriendsParty": "Inviting friends to your Party will grant you an exclusive <br/> Quest Scroll to battle the Basi-List together!",
"inviteFriendsParty": "Invite another player to your Party<br/> and receive the exclusive Basi-List Quest Scroll.",
"createParty": "Create a Party",
"inviteMembersNow": "Would you like to invite members now?",
"playInPartyTitle": "Play Habitica in a Party!",
"playInPartyDescription": "Take on amazing quests with friends or on your own. Battle monsters, create Challenges, and help yourself stay accountable through Parties.",
"wantToJoinPartyTitle": "Want to join a Party?",
"playInPartyDescription": "Take on amazing Quests with friends or on your own. Battle monsters, create Challenges, and help yourself stay accountable through Parties.",
"wantToJoinPartyTitle": "Looking for a Party?",
"wantToJoinPartyDescription": "Give your username to a friend who already has a Party, or head to the <a href='/groups/guild/f2db2a7f-13c5-454d-b3ee-ea1f5089e601'>Party Wanted Guild</a> to meet potential comrades!",
"lookForParty": "Look for a Party",
"currentlyLookingForParty": "Youre looking for a Party!",
"partyFinderDescription": "Want to join a Party with others but dont know any other players? Let Party leaders know youre looking for an invite!",
"copy": "Copy",
"questOwnerRewards": "Quest Owner Rewards",
"updateParty": "Update Party",
@@ -402,5 +409,13 @@
"newGroupsBullet10b": "<strong>Assign a task to one member</strong> so only they can complete it",
"newGroupsBullet10c": "<strong>Assign a task to multiple members</strong> if they all need to complete it",
"newGroupsVisitFAQ": "Visit the <a href='/static/faq#group-plans' target='_blank'>FAQ</a> from the Help dropdown for more guidance.",
"newGroupsEnjoy": "We hope you enjoy the new Group Plans experience!"
"newGroupsEnjoy": "We hope you enjoy the new Group Plans experience!",
"checkinsLabel": "Check-ins:",
"classLabel": "Class:",
"languageLabel": "Language:",
"invitedToYourParty": "<strong>Invited to Your Party!</strong>&nbsp;&nbsp;Click to Undo",
"lookingForPartyTitle": "Find Members",
"findMorePartyMembers": "Find More Members",
"findPartyMembers": "Find Party Members",
"noOneLooking": "Theres no one looking for a Party right now.<br>You can check back later!"
}
+5 -1
View File
@@ -194,7 +194,11 @@
"winter2023WalrusWarriorSet": "Walrus (Warrior)",
"winter2023FairyLightsMageSet": "Fairy Lights (Mage)",
"winter2023CardinalHealerSet": "Cardinal (Healer)",
"winter2023RibbonRogueSet": "Ribbon (Rogue)",
"winter2023RibbonRogueSet": "Ribbon (Rogue)",
"spring2023CaterpillarRogueSet": "Caterpillar (Rogue)",
"spring2023HummingbirdWarriorSet": "Hummingbird (Warrior)",
"spring2023MoonstoneMageSet": "Moonstone (Mage)",
"spring2023LilyHealerSet": "Lily (Healer)",
"eventAvailability": "Available for purchase until <%= date(locale) %>.",
"eventAvailabilityReturning": "Available for purchase until <%= availableDate(locale) %>. This potion was last available in <%= previousDate(locale) %>.",
"dateEndJanuary": "January 31",
+1 -1
View File
@@ -108,7 +108,7 @@
"toDo": "To Do",
"tourStatsPage": "This is your Stats page! Earn achievements by completing the listed tasks.",
"tourTavernPage": "Welcome to the Tavern, an all-ages chat room! You can keep your Dailies from hurting you in case of illness or travel by clicking \"Pause Damage\". Come say hi!",
"tourPartyPage": "Your Party will help you stay accountable. Invite friends to unlock a Quest Scroll!",
"tourPartyPage": "Welcome to your new Party! You can invite other players to your Party by username, email, or from a list of players looking for a Party to earn the exclusive Basi-List Quest Scroll.<br/><br/>Select <a href='/static/faq#parties'>FAQ</a> from the Help dropdown to learn more about how Parties work.",
"tourGuildsPage": "Guilds are common-interest chat groups created by the players, for the players. Browse through the list and join the Guilds that interest you. Be sure to check out the popular Habitica Help: Ask a Question guild, where anyone can ask questions about Habitica!",
"tourChallengesPage": "Challenges are themed task lists created by users! Joining a Challenge will add its tasks to your account. Compete against other users to win Gem prizes!",
"tourMarketPage": "Every time you complete a task, you'll have a random chance at receiving an Egg, a Hatching Potion, or a piece of Pet Food. You can also buy these items here.",
+1
View File
@@ -75,6 +75,7 @@
"chatExtensionDesc": "The Chat Extension for Habitica adds an intuitive chat box to all of habitica.com. It allows users to chat in the Tavern, their party, and any guilds they are in.",
"otherExtensions": "<a target='blank' href='https://habitica.fandom.com/wiki/Extensions,_Add-Ons,_and_Customizations'>Other Extensions</a>",
"otherDesc": "Find other apps, extensions, and tools on the Habitica wiki.",
"thirdPartyTools": "Find third party apps, extensions, and all kinds of other tools you can use with your account on the <a href='https://habitica.fandom.com/wiki/Extensions,_Add-Ons,_and_Customizations' target='_blank'>Habitica wiki</a>.",
"resetDo": "Do it, reset my account!",
"resetComplete": "Reset complete!",
"fixValues": "Fix Values",
@@ -148,6 +148,8 @@
"mysterySet202301": "Valiant Vulpine Set",
"mysterySet202302": "Trickster Tabby Set",
"mysterySet202303": "Mane Character Set",
"mysterySet202304": "Tiptop Teapot Set",
"mysterySet202305": "Eventide Dragon Set",
"mysterySet301404": "Steampunk Standard Set",
"mysterySet301405": "Steampunk Accessories Set",
"mysterySet301703": "Peacock Steampunk Set",
+1 -1
View File
@@ -312,7 +312,7 @@
"teamBasedTasksList": "Team-Based Task List",
"teamBasedTasksListDesc": "Set up an easily-viewed shared task list for the group. Assign tasks to your fellow group members, or let them claim their own tasks to make it clear what everyone is working on!",
"groupManagementControls": "Group Management Controls",
"groupManagementControlsDesc": "Use task approvals to verify that a task that was really completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"groupManagementControlsDesc": "View task status to verify that a task that was completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"inGameBenefits": "In-Game Benefits",
"inGameBenefitsDesc": "Group members get an exclusive Jackalope Mount, as well as full subscription benefits, including special monthly equipment sets and the ability to buy gems with gold.",
"inspireYourParty": "Inspire your party, gamify life together.",
+1 -1
View File
@@ -312,7 +312,7 @@
"teamBasedTasksList": "Team-Based Task List",
"teamBasedTasksListDesc": "Set up an easily-viewed shared task list for the group. Assign tasks to your fellow group members, or let them claim their own tasks to make it clear what everyone is working on!",
"groupManagementControls": "Group Management Controls",
"groupManagementControlsDesc": "Use task approvals to verify that a task that was really completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"groupManagementControlsDesc": "View task status to verify that a task that was completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"inGameBenefits": "In-Game Benefits",
"inGameBenefitsDesc": "Squadron members git an exclusive Jackalope Steed, as well as full subscr'ption benefits, includin' special monthly equipment sets an' th' ability t' buy sapphires wit' gold.",
"inspireYourParty": "Inspire yer crew, gamify life togeth'r.",
+1 -1
View File
@@ -312,7 +312,7 @@
"teamBasedTasksList": "Team-Based Task List",
"teamBasedTasksListDesc": "Set up an easily-viewed shared task list for the group. Assign tasks to your fellow group members, or let them claim their own tasks to make it clear what everyone is working on!",
"groupManagementControls": "Group Management Controls",
"groupManagementControlsDesc": "Use task approvals to verify that a task that was really completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"groupManagementControlsDesc": "View task status to verify that a task that was completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"inGameBenefits": "In-Game Benefits",
"inGameBenefitsDesc": "Group members get an exclusive Jackalope Mount, as well as full subscription benefits, including special monthly equipment sets and the ability to buy gems with gold.",
"inspireYourParty": "Inspire your party, gamify life together.",
+1 -1
View File
@@ -312,7 +312,7 @@
"teamBasedTasksList": "Team-Based Task List",
"teamBasedTasksListDesc": "Set up an easily-viewed shared task list for the group. Assign tasks to your fellow group members, or let them claim their own tasks to make it clear what everyone is working on!",
"groupManagementControls": "Group Management Controls",
"groupManagementControlsDesc": "Use task approvals to verify that a task that was really completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"groupManagementControlsDesc": "View task status to verify that a task that was completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"inGameBenefits": "In-Game Benefits",
"inGameBenefitsDesc": "Group members get an exclusive Jackalope Mount, as well as full subscription benefits, including special monthly equipment sets and the ability to buy gems with gold.",
"inspireYourParty": "Inspire your party, gamify life together.",
+4 -1
View File
@@ -144,5 +144,8 @@
"achievementPolarPro": "Experto Polar",
"achievementPolarProModalText": "¡Has coleccionado todas las mascotas Polares!",
"achievementBoneToPickModalText": "¡Has coleccionado todas las mascotas clásicas y de misiones esqueléticas!",
"achievementPolarProText": "¡Ha eclosionado todos los colores estándar para mascotas Polares: Osos, Zorros, Pinguinos, Ballenas y Lobos!"
"achievementPolarProText": "¡Ha eclosionado todos los colores estándar para mascotas Polares: Osos, Zorros, Pinguinos, Ballenas y Lobos!",
"achievementPlantParent": "Progenitor de las Plantas",
"achievementPlantParentText": "¡Ha eclosionado todos los colores estándar para las mascotas Planta: Cáctus y Esqueje de árbol!",
"achievementPlantParentModalText": "¡Has coleccionado todas las Mascotas Planta!"
}
+7 -2
View File
@@ -675,7 +675,7 @@
"backgroundOrangeGroveText": "Naranjal",
"backgroundOrangeGroveNotes": "Deambula por un naranjal fragante.",
"backgrounds022022": "93.ª serie: publicada en febrero de 2022",
"backgrounds032022": "94.ª serie: publiccada en marzo de 2022",
"backgrounds032022": "94.ª serie: publicada en marzo de 2022",
"backgroundBrickWallWithIvyText": "Pared de Ladrillo con Hiedra",
"backgroundBrickWallWithIvyNotes": "Admira una Pared de Ladrillo con Hiedra.",
"backgrounds042022": "95ª. serie: publicada en abril de 2022",
@@ -762,5 +762,10 @@
"backgrounds112022": "102ª serie: publicada en noviembre del 2022",
"backgroundAmongGiantMushroomsText": "Entre setas gigantes",
"backgroundAmongGiantMushroomsNotes": "Maravillate con las setas gigantes.",
"backgroundSnowyVillageNotes": "Admira un pueblo nevado."
"backgroundSnowyVillageNotes": "Admira un pueblo nevado.",
"backgroundOldTimeyBasketballCourtNotes": "Echa unas canastas en una cancha de baloncesto antigua.",
"backgroundMangroveForestText": "Bosque de Manglares",
"backgroundMangroveForestNotes": "Explora las lindes del Bosque de Manglares.",
"backgrounds032023": "106.ª serie: publicada en marzo de 2023",
"backgroundOldTimeyBasketballCourtText": "Cancha de Baloncesto Antigua"
}
@@ -17,42 +17,42 @@
"commGuideList02G": "<strong>Cumple inmediatamente con cualquier solicitud de un mod</strong>. Esto podría incluir, entre otras cosas, que te pida limitar tus publicaciones en un espacio en particular, editar tu perfil para eliminar contenido inadecuado, pedirte que traslades un debate a un espacio más adecuado, etc. No discutas con la Administración. Si tienes alguna preocupación o comentario sobre la moderación, envía un email a <a href='mailto:admin@habitica.com' target='_blank'>admin@habitica.com</a> para contactar con nuestro community manager.",
"commGuideList02J": "<strong>No mandes correo no deseados</strong>. Esto puede incluir, entre otros: publicar el mismo comentario o consulta en varios lugares, <strong>publicar enlaces sin explicación ni contexto</strong>, publicar mensajes sin sentido, publicar varios mensajes promocionales sobre un Gremio, Grupo o Desafío, o publicar muchos mensajes seguidos. Si te beneficias de que la gente haga clic en un enlace, debes explicarlo en el texto del mensaje o también se considerará correo no deseado. Los mods pueden decidir qué constituye correo no deseado a su discreción.",
"commGuideList02K": "<strong>Evita publicar encabezados grandes en los espacios públicos, especialmente en la Taberna</strong>. Al igual que TODO EN MAYÚSCULA, se lee como si estuvieras gritando, e interfiere con un ambiente cómodo.",
"commGuideList02L": "<strong>Desaconsejamos encarecidamente el intercambio de información personal, en particular, información que pueda utilizarse para identificarte, en espacios públicos</strong>. La información identificadora puede incluir, entre otros: tu dirección personal, tu dirección de correo electrónico y tu token de API/contraseña. ¡Esto es por tu seguridad! El personal o los moderadores pueden eliminar tales publicaciones a su discreción. Si se te solicita información personal en un Gremio, Equipo o MP, recomendamos encarecidamente que lo rechaces educadamente y avises al personal y a los moderadores de alguna de las siguientes maneras: 1) marcando el mensaje, o 2) enviando un correo a <a href='mailto:admin@habitica.com' target='_blank'>admin@habitica.com</a> e incluyendo capturas de pantalla.",
"commGuideList02L": "<strong>Desaconsejamos encarecidamente el intercambio de información personal, en particular, información que pueda utilizarse para identificarte, en espacios públicos</strong>. La información identificadora puede incluir, entre otros: tu dirección personal, tu dirección de correo electrónico y tu token de API/contraseña. ¡Esto es por tu seguridad! El personal puede eliminar tales publicaciones a su discreción. Si se te solicita información personal en un Gremio, Equipo o MP, recomendamos encarecidamente que lo rechaces educadamente y avises al personal de cualquiera de las siguientes maneras: 1) marcando el mensaje, o 2) enviando un correo a <a href='mailto:admin@habitica.com' target='_blank'>admin@habitica.com</a> e incluyendo capturas de pantalla.",
"commGuidePara019": "<strong>En espacios privados</strong>, los usuarios tienen más libertada para debatir los temas que deseen, pero aun así no deben violar los Términos y Condiciones, incluyendo colgar insultos o cualquier contenido discriminatorio, violento o amenazante. Tened en cuenta que, dado que los nombres de los Desafíos aparecen en el perfil público del ganador, TODOS los desafíos deben obedecer las Normas de Espacios Públicos, incluso si aparecen en espacios privados.",
"commGuidePara020": "<strong>Mensajes privados (PMs)</strong> tienen algunas reglas adicionales. Si alguien te ha bloqueado, no contactes con él a través de otro medio para pedir que te desbloquee. Tampoco envíes mensajes privados pidiendo ayuda (ya que las respuestas públicas a dudas o preguntas pueden ser de utilidad para toda la comunidad). Por último, no envíes mensajes privados mendigando contenido de pago de ningún tipo.",
"commGuidePara020A": "<strong>Si ves una publicación o un mensaje directo que crees que supone una violación de las Normas de Espacios Públicos, o si ves una publicación o un mensaje directo que te preocupa o incomoda, puedes llamar la atención de los Moderadores y del Personal sobre esa publicación haciendo click en el icono de la bandera roja para reportarlo</strong>. Un miembro del Personal o un Moderador responderá a la situación lo más pronto posible. Por favor, ten en cuenta que reportar intencionadamente publicaciones inocentes es una infracción de estas Normas (mira debajo en \"Infracciones\"). También puedes contactar a los Moderadores enviando un correo electrónico a <a href='mailto:admin@habitica.com' target='_blank'>admin@habitica.com</a>. Es preferible hacer esto si hay varias publicaciones problemáticas de la misma persona en distintos Gremios, o si la situación necesita explicación. Puedes contactarnos en tu idioma nativo si te resulta más fácil: es posible que tengamos que usar el Traductor de Google, pero queremos que te sientas cómodo contactando con nosotros si tienes un problema.",
"commGuidePara020A": "<strong>Si ves una publicación o un mensaje directo que crees que supone una violación de las Normas de Espacios Públicos, o si ves una publicación o un mensaje directo que te preocupa o incomoda, puedes llamar la atención del Personal sobre esa publicación haciendo click en el icono de la bandera roja para reportarlo</strong>. Un miembro del Personal responderá a la situación lo más pronto posible. Por favor, ten en cuenta que reportar intencionadamente publicaciones inocentes es una infracción de estas Normas (mira debajo en \"Infracciones\"). También puedes contactar al Personal enviando un correo electrónico a <a href='mailto:admin@habitica.com' target='_blank'>admin@habitica.com</a>. Es preferible hacer esto si hay varias publicaciones problemáticas de la misma persona en distintos Gremios, o si la situación necesita explicación. Puedes contactarnos en tu idioma nativo si te resulta más fácil: es posible que tengamos que usar el Traductor de Google, pero queremos que te sientas cómodo contactando con nosotros si tienes un problema.",
"commGuidePara021": "Además, algunos espacios públicos de Habitica tienen normas adicionales.",
"commGuideHeadingTavern": "La Taberna",
"commGuidePara022": "La Taberna es el lugar principal para que los Habiticanos socialicen. Daniel el tabernero mantiene el lugar limpio y ordenado, y, con gusto, Lemoness hará aparecer limonada mientras tú te sientas y charlas. Tan solo ten en cuenta…",
"commGuidePara023": "<strong>La conversación tiende a girar en torno a charlas casuales y consejos sobre productividad o cómo mejorar a vida</strong>. Debido a que la sala de chat de la Taberna solo puede contener 200 mensajes, <strong>no es un lugar apropiado para conversaciones prolongadas sobre algunos temas, especialmente los más delicados</strong> (por ejemplo: política, religión, depresión, si se debe vetar o no la caza de trasgos, etc.). Estas conversaciones deben llevarse a un Gremio de temática aplicable. Un Mod puede dirigirte a un Gremio adecuado, pero en última instancia es tu responsabilidad buscar y publicar en el lugar apropiado.",
"commGuidePara023": "<strong>La conversación tiende a girar en torno a charlas casuales y consejos sobre productividad o cómo mejorar a vida</strong>. Debido a que la sala de chat de la Taberna solo puede contener 200 mensajes, <strong>no es un lugar apropiado para conversaciones prolongadas sobre algunos temas, especialmente los más delicados o polémicos</strong> (por ejemplo: política, religión, depresión, si se debe vetar o no la caza de trasgos, etc.). Estas conversaciones deben llevarse a un Gremio de temática aplicable. El Personal puede dirigirte a un Gremio adecuado, pero en última instancia es tu responsabilidad buscar y publicar en el lugar apropiado.",
"commGuidePara024": "<strong>No converses sobre nada adictivo en la Taberna</strong>. Mucha gente usa Habitica para intentar dejar sus Malos Hábitos. ¡Escuchar a otros hablar de sustancias adictivas/ilegales puede hacer más difícil su propósito! Respeta a tus compañeros de la Taberna, y ten en cuenta esto. Incluye, pero no exclusivamente: fumar, alcohol, pornografía, juegos de apuesta, y uso/abuso de drogas.",
"commGuidePara027": "<strong>Cuando un moderador te indica que lleves una conversación a otro lugar, si no hay un Gremio relacionado, es posible que te sugiera utilizar el Rincón Trasero</strong>. El \"Back Corner Guild\" es un espacio público gratuito para debatir sobre temas potencialmente delicados que solo debería usarse cuando un moderador lo dirija. Es monitoreado cuidadosamente por el equipo de moderación. No es un lugar para discusiones generales o conversaciones, y un mod te dirigirá allí solo cuando sea apropiado.",
"commGuideHeadingPublicGuilds": "Gremios Públicos",
"commGuidePara029": "<strong>Los Gremios Públicos se parecen mucho a la Taberna, con la excepción de que en lugar de centrarse en conversaciones generales, tienen un tema central</strong>. La sala de chat de los Gremios Públicos deben enfocarse en estos temas. Por ejemplo, los miembros del gremio de \"Wordsmiths Guild\" podrían enfadarse si la conversación se centra repentinamente en la jardinería en lugar de en la escritura, y un gremio de Fanáticos del Dragón podría no mostrar interés en descifrar runas antiguas. Algunos Gremios se muestran más permisivos con este tema que otros, pero en general, <strong>¡trata de no salirte del tema</strong>!",
"commGuidePara031": "En algunos Gremios públicos se tratan temas delicados como la depresión, la religión, la política, etc. Esto no supone un problema siempre que las conversaciones no infrinjan los Términos y Condiciones ni las Normas de Espacios Públicos, y siempre que lo que se hable sea relevante.",
"commGuidePara033": "<strong>Los Gremios Públicos NO pueden contener contenidos para 18+. Si planean debatir regularmente contenido sensible, deberían decirlo en la descripción del Gremio</strong>. Esto sirve para mantener Habitica segura y cómoda para todos.",
"commGuidePara035": "<strong>Si en el Gremio en cuestión se tratan cuestiones delicadas de cualquier tipo, es respetuoso para con tus compañeros Habiticanos publicar tu comentario a continuación de una advertencia (por ejemplo: \"Advertencia: referencias a autolesión\")</strong>. Estas pueden presentarse como mensajes de advertencia y/o notas sobre el contenido, y los Gremios pueden tener sus propias reglas además de las que se dan aquí. Si es posible, utiliza <a href='https://habitica.fandom.com/es/wiki/Gu%C3%ADa_de_Markdown' target='_blank'>lenguaje de marcado</a> para ocultar el contenido potencialmente sensible bajo varios saltos de línea, de modo que aquellos que deseen evitar leerlo puedan pasarlo por alto sin ver el contenido. En cualquier caso, el personal de Habitica y los moderadores pueden decidir eliminar este material a su discreción.",
"commGuidePara035": "<strong>Si en el Gremio en cuestión se tratan cuestiones delicadas de cualquier tipo, es respetuoso para con tus compañeros Habiticanos incluir una advertencia (por ejemplo: \"Advertencia: referencias a autolesión\")</strong>. Estas pueden presentarse como mensajes de advertencia y/o notas sobre el contenido, y los Gremios pueden tener sus propias reglas además de las que se dan aquí. En cualquier caso, el personal de Habitica puede decidir eliminar este material a su discreción.",
"commGuidePara036": "Además, el material sensible debe estar relacionado la temática del Gremio: sacar el tema de la autolesión en un Gremio centrado en la lucha contra la depresión puede tener sentido, pero probablemente sea menos apropiado en un Gremio musical. Si ves que alguien infringe reiteradamente esta directriz, especialmente después de haberle llamado la atención, te rogamos que reportes la publicación.",
"commGuidePara037": "<strong>Ningún Gremio, ni Público ni Privado, debe ser creado con el propósito de atacar a un grupo o individuo</strong>. La creación de un Gremio así es razón para ser expulsado inmediatamente. ¡Lucha contra los malos hábitos, no contra tus compañeros de aventura!",
"commGuidePara038": "<strong>Todos los Retos de Taberna y los Retos de los Gremios Públicos deben ceñirse a estas reglas asímismo</strong>.",
"commGuideHeadingInfractionsEtc": "Infracciones, Consecuencias y Restauración",
"commGuideHeadingInfractions": "Infracciones",
"commGuidePara050": "Una mayoría abrumadora de Habiticanos se ayudan entre sí, son respetuosos, y procuran que la comunidad entera sea divertida y amistosa. Sin embargo, muy raramente, algo de lo que hace un Habiticano puede violar una de los normas anteriores. Cuando esto sucede, los Moderadores tomarán cualquier acción que crean necesaria para asegurar que Habitica es un lugar seguro y cómodo para todos.",
"commGuidePara051": "<strong>Hay varios tipos de infracciones, y se tratan dependiendo de su gravedad</strong>. Estas no son listas exhaustivas, y los Mods pueden tomar decisiones en temas no registrados aquí bajo su discreción. Los Mods tendrán en cuenta el contexto al evaluar las infracciones.",
"commGuidePara050": "Una mayoría abrumadora de Habiticanos se ayudan entre sí, son respetuosos, y procuran que la comunidad entera sea divertida y amistosa. Sin embargo, muy raramente, algo de lo que hace un Habiticano puede violar una de los normas anteriores. Cuando esto sucede, el Personal tomará cualquier acción que crea necesaria para asegurar que Habitica es un lugar seguro y cómodo para todos.",
"commGuidePara051": "<strong>Hay varios tipos de infracciones, y se tratan dependiendo de su gravedad</strong>. Estas no son listas exhaustivas, y los Mods pueden tomar decisiones en temas no registrados aquí bajo su discreción. El Personal tendrá en cuenta el contexto al evaluar las infracciones.",
"commGuideHeadingSevereInfractions": "Infracciones graves",
"commGuidePara052": "Infracciones graves dañan enormemente la seguridad de la comunidad y usuarios de Habitica, y por lo tanto llevan consecuencias graves como resultado.",
"commGuidePara053": "Los siguientes son algunos ejemplos de infracciones graves. Esta no es una lista completa.",
"commGuideList05A": "Violación de los Términos y Condiciones",
"commGuideList05B": "Discursos de odio/imágenes , acoso, cyber-bullying , mensaje abusivos/repetitivos y trolear",
"commGuideList05C": "Violación de Libertad Condicional",
"commGuideList05D": "Suplantar al Personal o a los Moderadores - esto incluye hacer pasar como reales espacios no afiliados oficialmente a Habitica o moderados por sus administradores ni personal",
"commGuideList05D": "Suplantar al Personal - esto incluye hacer pasar como reales espacios no afiliados oficialmente a Habitica o moderados por su Personal",
"commGuideList05E": "Infracciones moderadas repetidas",
"commGuideList05F": "Creación de una cuenta duplicada para evitar consecuencias (por ejemplo, crear una cuenta para hablar por chat después de que los privilegios de chat hayan sido revocados)",
"commGuideList05G": "Engaño intencionado al Personal o a los Moderadores para evitar consecuencias o para meter en problemas a otro usuario",
"commGuideList05G": "Engaño intencionado al Personal para evitar consecuencias o para meter en problemas a otro usuario",
"commGuideHeadingModerateInfractions": "Infracciones moderadas",
"commGuidePara054": "Infracciones moderadas no hacen a nuestra comunidad insegura, pero la hacen desagradable. Estas infracciones tendrán consecuencias moderadas. En relación con infracciones múltiples, las consecuencias pueden ser más graves.",
"commGuidePara055": "Los siguientes son algunos ejemplos de infracciones moderadas. Esto no es una lista completa.",
"commGuideList06A": "Ignorar, faltar al respeto o discutir con un Moderador. Esto incluye protestar públicamente acerca de moderadores u otros usuarios, glorificar o defender públicamente a usuarios vetados, o debatir si las medidas tomadas por un moderador son apropiadas o no. Si estás preocupado por alguna norma o el comportamiento de los Mods, por favor, contacta el personal por correo (<a href='mailto:admin@habitica.com' target='_blank'>admin@habitica.com</a>).",
"commGuideList06A": "<strong>Ignorar, faltar al respeto o discutir con el Personal. Esto incluye protestar públicamente acerca del personal u otros usuarios, glorificar o defender públicamente a usuarios vetados, o debatir si las medidas tomadas por el personal son apropiadas o no. Si estás preocupado por alguna norma o el comportamiento del Personal, por favor, contacta con nosotros por correo electrónico (<a href='mailto:admin@habitica.com' target='_blank'>admin@habitica.com</a>).",
"commGuideList06B": "Modificación en segundo plano. Vamos a aclarar rápidamente un punto relevante: Una mención amistosa de las normas está bien. La modificación en segundo plano consiste en decir, demandar, y/o insinuar con insistencia que alguien debe hacer algo que tú describes para corregir un error. Puedes alertar a otras personas sobre el hecho de que han cometido una transgresión, pero, por favor, no exijas una actuación; como por ejemplo, decir: \"Debes saber que se desaconseja blasfemar en la Taberna, por lo que es posible que quieras eliminar eso\", sería mejor opción que decir: \"voy a tener que pedirte que borres ese mensaje.\"",
"commGuideList06C": "Marcar intencionadamente publicaciones inocentes.",
"commGuideList06D": "Violar repetidamente de las Normas de Espacios Públicos",
@@ -61,12 +61,12 @@
"commGuidePara056": "Las Infracciones menores, si bien son desaconsejadas, tienen consecuencias menores. Si continúan ocurriendo, con el tiempo pueden conducir a consecuencias más severas.",
"commGuidePara057": "Los siguientes son algunos ejemplos de infracciones menores. Esta no es una lista completa.",
"commGuideList07A": "Primera Violación de las Normas de Espacios Públicos",
"commGuideList07B": "Cualquier declaración o acción que provoque un \"Por favor, no...\". Cuando se te pida que no hagas algo públicamente, este hecho en sí mismo puede ser una consecuencia. Si los moderadores teiene que emitir varias de estas correcciones a la misma persona, podrán contar como una infracción más grave",
"commGuideList07B": "Cualquier declaración o acción que provoque un \"Por favor, no...\" por parte del Personal. Cuando se te pida que no hagas algo públicamente, este hecho en sí mismo puede ser una consecuencia. Si el Personal tiene que emitir varias de estas correcciones a la misma persona, podrán contar como una infracción más grave",
"commGuidePara057A": "Algunas publicaciones pueden estar ocultas porque contienen información sensible o pueden dar a las personas una idea equivocada. Por lo general, esto no cuenta como una infracción, ¡sobre todo no si es la primera vez que ocurre!",
"commGuideHeadingConsequences": "Consecuencias",
"commGuidePara058": "En Habitica -- así como en la vida real -- cada acción tiene su consecuencia, si se trata de ponerse en forma por correr, tener caries por comer demasiado asúcar, o sobresaliendo en una clase por estudiar.",
"commGuidePara059": "<strong>Del mismo modo, cada infracción tiene consecuencias directas.</strong> Algunos ejemplos se resumen abajo.",
"commGuidePara060": "<strong>Si su infracción tiene una consecuencia moderada o severa, habrá una publicación de los miembros del personal o de los moderadores en el foro en el que ha ocurrido la infracción que explicará</strong>:",
"commGuidePara060": "<strong>Si su infracción tiene una consecuencia moderada o severa y es adecuado a las circunstancias, habrá una publicación de los miembros del personal en el foro en el que ha ocurrido la infracción que explicará</strong>:",
"commGuideList08A": "En qué consistió tu infracción",
"commGuideList08B": "cuál es su consecuencia",
"commGuideList08C": "qué hacer para corregir la situación y restablecer tu status, si es posible.",
@@ -77,7 +77,7 @@
"commGuideList09C": "Desabilitando permanentemente (\"congelando\") progreso por Niveles de Colaboradores",
"commGuideHeadingModerateConsequences": "Ejemplos de Consecuencias Moderadas",
"commGuideList10A": "Privilegios restringidos en las salas de chat públicas y/o privadas",
"commGuideList10A1": "Si sus acciones resultan en la revocación de sus privilegios de la sala chat, un Moderador o Miembro del personal te enviará un PM y/o publicará en el foro en el que te silenciaron para notificarte el motivo por el que se te ha silenciado y el período de tiempo durante el cual estarás silenciado y/o la acción requerida para que ser restituído. Serás restituido si cumples educadamente con las acciones requeridas y te comprometes a cumplir con las Normas de la Comunidad y los Términos de Servicio (ToS)",
"commGuideList10A1": "Si sus acciones resultan en la revocación de sus privilegios de la sala chat deberás enviar un correco electrónico a you must email <a href='mailto:admin@habitica.com' target='_blank'>admin@habitica.com</a>. Tus privilegios podrán restituirse si el Personal decide que cumples educadamente con las acciones requeridas y te comprometes a cumplir con las Normas de la Comunidad y los Términos de Servicio (ToS)",
"commGuideList10C": "Privilegios restringidos en la creación de Gremios/Desafíos",
"commGuideList10D": "Desabilitando temporalmente (\"congelando\") progreso por Niveles de Colaboradores",
"commGuideList10E": "Descenso de Nivel de Contribuyente",
@@ -86,15 +86,15 @@
"commGuideList11A": "Recordatorios de las Normas de Espacios Públicos",
"commGuideList11B": "Advertencias",
"commGuideList11C": "Peticiones",
"commGuideList11D": "Eliminación (Puede que los Moderadores/Personal borren contenido controvertido)",
"commGuideList11E": "Ediciones (Puede que los Moderadores/Personal editen contenido controvertido)",
"commGuideList11D": "Eliminación (Puede que el Personal borre contenido controvertido)",
"commGuideList11E": "Ediciones (Puede que el Personal edite contenido controvertido)",
"commGuideHeadingRestoration": "Restauración",
"commGuidePara061": "Habitica es una tierra dedicada a la superación personal, y creemos en las segundas oportunidades. <strong>Si cometes una infracción y sufres las consecuencia, visualízala como una oportunidad para evaluar tus acciones y para esforzarte por ser un mejor miembro de la comunidad</strong>.",
"commGuidePara062": "El anuncio, mensaje y/o correo electrónico que recibes explicando las consecuencias de tus acciones es una buena fuente de información. Coopera con cualquier restricción que se te haya impuesto y trata de cumplir con los requisitos para que se eliminen las sanciones.",
"commGuidePara063": "Si no comprendes las consecuencias o la naturaleza de tus infracciones, pregunta al Personal/Moderador para que te ayuden y así evitar cometer infracciones en el futuro. Si sientes que una decision en particular ha sido injusta, puedes ponerte en contacto con el personal para debatirlo en <a href='mailto:admin@habitica.com' target='_blank'>admin@habitica.com</a>.",
"commGuideHeadingMeet": "¡Conoce al Personal y a los Moderadores!",
"commGuidePara063": "Si no comprendes las consecuencias o la naturaleza de tus infracciones, pregunta al Personal para que te ayude y así evitar cometer infracciones en el futuro. Si sientes que una decisión en particular ha sido injusta, puedes ponerte en contacto con el personal para debatirlo en <a href='mailto:admin@habitica.com' target='_blank'>admin@habitica.com</a>.",
"commGuideHeadingMeet": "¡Conoce al Personal!",
"commGuidePara006": "Habitica cuenta con algunos paladines errantes incansables que unen sus fuerzas a los miembros del personal para mantener la comunidad en calma, contenta y libre de trolls. Cada uno cuenta con un dominio específico, pero a veces se les llama a servir en otras esferas sociales.",
"commGuidePara007": "El personal de Habitica tiene etiquetas violetas marcadas con coronas. Su titulo es \"Heroico\".",
"commGuidePara007": "El Personal de Habitica se ocupa de que la app y las páginas funcionen bien, y además cumple la función de moderación del chat. Tiene etiquetas violetas marcadas con coronas. Su titulo es \"Heroico\".",
"commGuidePara008": "Los moderadores tienen etiquetas azul oscuro acompañadas de una estrella. Su título es \"Guardián\".",
"commGuidePara009": "Los actuales miembros del personal son (de izquierda a derecha):",
"commGuideAKA": "<%= habitName %> también conocido como <%= realName %>",
+2 -1
View File
@@ -372,5 +372,6 @@
"hatchingPotionSolarSystem": "Sistema solar",
"hatchingPotionOnyx": "Ónice",
"hatchingPotionVirtualPet": "Mascota virtual",
"hatchingPotionPorcelain": "Porcelana"
"hatchingPotionPorcelain": "Porcelana",
"hatchingPotionPinkMarble": "Mármol Rosa"
}
+60 -60
View File
@@ -1632,7 +1632,7 @@
"headAccessorySpecialSpring2015MageText": "Orejas de Conejito Azul",
"headAccessorySpecialSpring2015MageNotes": "Estas orejas escuchan atentamente, en el caso de que algún mago esté revelando algún secreto. No confieren ningún beneficio. Equipamiento de edición limitada de primavera 2015.",
"headAccessorySpecialSpring2015HealerText": "Orejas de Gatito Verdes",
"headAccessorySpecialSpring2015HealerNotes": "Estas adorables orejas harán que los demás se pongan verdes de envidia. No otorga ningún beneficio. Equipamiento de Verano Edición Limitada del 2015.",
"headAccessorySpecialSpring2015HealerNotes": "Estas adorables orejas harán que los demás se pongan verdes de envidia. No otorga ningún beneficio. Equipamiento de edición limitada de verano 2015.",
"headAccessorySpecialSpring2016RogueText": "Orejas de Perro Verdes",
"headAccessorySpecialSpring2016RogueNotes": "¡Con éstas podrás mantener a astutos magos en la mira aunque se vuelvan invisibles! No otorgan ningún beneficio. Equipamiento de edición limitada de primavera 2016.",
"headAccessorySpecialSpring2016WarriorText": "Orejas de Ratón Rojas",
@@ -1640,46 +1640,46 @@
"headAccessorySpecialSpring2016MageText": "Orejas de Gato Amarillas",
"headAccessorySpecialSpring2016MageNotes": "Estas puntiagudas orejas pueden detectar el insignificante zumbido del Mana ambiente, o las silenciosas pisadas de un Pícaro. No otorga ningún beneficio. Equipamiento de edición limitada de primavera 2016.",
"headAccessorySpecialSpring2016HealerText": "Orejas de Conejito Moradas",
"headAccessorySpecialSpring2016HealerNotes": "Altas como banderas en una batalla, permiten a otros ver dónde conseguir ayuda. No confieren ningún beneficio. Equipamiento de Edición Limitada de Primavera 2016.",
"headAccessorySpecialSpring2016HealerNotes": "Altas como banderas en una batalla, permiten a otros ver dónde conseguir ayuda. No otorga ningún beneficio. Equipamiento de edición limitada de primavera 2016.",
"headAccessorySpecialSpring2017RogueText": "Orejas de Conejito Rojas",
"headAccessorySpecialSpring2017RogueNotes": "No se te escapará ningún sonido gracias a estas orejas. No proporciona ningún beneficio. Equipo de Edición Limitada Primavera 2017.",
"headAccessorySpecialSpring2017RogueNotes": "No se te escapará ningún sonido gracias a estas orejas. No otorga ningún beneficio. Equipamiento de edición limitada de primavera 2017.",
"headAccessorySpecialSpring2017WarriorText": "Orejas de Gatito Azul",
"headAccessorySpecialSpring2017WarriorNotes": "¡Estas orejas pueden oír el sonido de una bolsa de golosinas gatunas abriéndose incluso en el ardor de la batalla! No proporciona ningún beneficio. Equipo de Edición Limitada Primavera 2017.",
"headAccessorySpecialSpring2017WarriorNotes": "¡Estas orejas pueden oír el sonido de una bolsa de golosinas gatunas abriéndose incluso en el ardor de la batalla! No otorga ningún beneficio. Equipamiento de edición limitada de primavera 2017.",
"headAccessorySpecialSpring2017MageText": "Orejas de Perro Verde Mar",
"headAccessorySpecialSpring2017MageNotes": "¡Puedes oír la magia del aire! No proporciona ningún beneficio. Equipo de Edición Limitada Primavera 2017.",
"headAccessorySpecialSpring2017MageNotes": "¡Puedes oír la magia del aire! No otorga ningún beneficio. Equipamiento de edición limitada de primavera 2017.",
"headAccessorySpecialSpring2017HealerText": "Orejas de Ratón Moradas",
"headAccessorySpecialSpring2017HealerNotes": "Estas orejas te permitirán escuchar secretos. No proporcionan ningún beneficio. Equipamiento de Edición Limitada Primavera 2017.",
"headAccessoryBearEarsText": "Orejas de oso",
"headAccessorySpecialSpring2017HealerNotes": "Estas orejas te permitirán escuchar secretos. No otorga ningún beneficio. Equipamiento de edición limitada de primavera 2017.",
"headAccessoryBearEarsText": "Orejas de Oso",
"headAccessoryBearEarsNotes": "¡Estas orejas te hacen parecer un oso valiente! No otorga ningún beneficio.",
"headAccessoryCactusEarsText": "Orejas de cactus",
"headAccessoryCactusEarsText": "Orejas de Cactus",
"headAccessoryCactusEarsNotes": "¡Estas orejas te hacen parecer un cactus espinoso! No otorga ningún beneficio.",
"headAccessoryFoxEarsText": "Orejas de Zorro",
"headAccessoryFoxEarsNotes": "Con estas orejas, parecerás un astuto zorro. No aportan ningún beneficio.",
"headAccessoryFoxEarsNotes": "Con estas orejas, parecerás un astuto zorro. No otorga ningún beneficio.",
"headAccessoryLionEarsText": "Orejas de Leon",
"headAccessoryLionEarsNotes": "Con estas orejas, parecerás un majestuoso león. No aportan ningún beneficio.",
"headAccessoryLionEarsNotes": "Con estas orejas, parecerás un majestuoso león. No otorga ningún beneficio.",
"headAccessoryPandaEarsText": "Orejas de Panda",
"headAccessoryPandaEarsNotes": "Con estas orejas, parecerás un amable panda. No aportan ningún beneficio.",
"headAccessoryPandaEarsNotes": "Con estas orejas, parecerás un amable panda. No otorga ningún beneficio.",
"headAccessoryPigEarsText": "Orejas de Cerdo",
"headAccessoryPigEarsNotes": "Con estas orejas, parecerás un caprichoso porcino. No aportan ningún beneficio.",
"headAccessoryPigEarsNotes": "Con estas orejas, parecerás un caprichoso porcino. No otorga ningún beneficio.",
"headAccessoryTigerEarsText": "Orejas de Tigre",
"headAccessoryTigerEarsNotes": "Con estas orejas, parecerás un feroz tigre. No aportan ningún beneficio.",
"headAccessoryTigerEarsNotes": "Con estas orejas, parecerás un feroz tigre. No otorga ningún beneficio.",
"headAccessoryWolfEarsText": "Orejas de Lobo",
"headAccessoryWolfEarsNotes": "Con estas orejas, parecerás un leal lobo. No aportan ningún beneficio.",
"headAccessoryBlackHeadbandText": "Diadema negra",
"headAccessoryWolfEarsNotes": "Con estas orejas, parecerás un leal lobo. No otorga ningún beneficio.",
"headAccessoryBlackHeadbandText": "Diadema Negra",
"headAccessoryBlackHeadbandNotes": "Una diadema negra sencilla. No otorga ningún beneficio.",
"headAccessoryBlueHeadbandText": "Diadema azul",
"headAccessoryBlueHeadbandText": "Diadema Azul",
"headAccessoryBlueHeadbandNotes": "Una diadema azul sencilla. No otorga ningún beneficio.",
"headAccessoryGreenHeadbandText": "Diadema verde",
"headAccessoryGreenHeadbandText": "Diadema Verde",
"headAccessoryGreenHeadbandNotes": "Una diadema verde sencilla. No otorga ningún beneficio.",
"headAccessoryPinkHeadbandText": "Diadema rosa",
"headAccessoryPinkHeadbandText": "Diadema Rosa",
"headAccessoryPinkHeadbandNotes": "Una diadema rosa sencilla. No otorga ningún beneficio.",
"headAccessoryRedHeadbandText": "Diadema roja",
"headAccessoryRedHeadbandText": "Diadema Roja",
"headAccessoryRedHeadbandNotes": "Una diadema roja sencilla. No otorga ningún beneficio.",
"headAccessoryWhiteHeadbandText": "Diadema blanca",
"headAccessoryWhiteHeadbandText": "Diadema Blanca",
"headAccessoryWhiteHeadbandNotes": "Una diadema blanca sencilla. No otorga ningún beneficio.",
"headAccessoryYellowHeadbandText": "Diadema amarilla",
"headAccessoryYellowHeadbandText": "Diadema Amarilla",
"headAccessoryYellowHeadbandNotes": "Una diadema amarilla sencilla. No otorga ningún beneficio.",
"headAccessoryMystery201403Text": "Astas del caminante del bosque",
"headAccessoryMystery201403Text": "Astas del Caminante del Bosque",
"headAccessoryMystery201403Notes": "Estos cuernos brillan con musgo y líquenes. No otorga ningún beneficio. Artículo de suscriptor. Marzo 2014.",
"headAccessoryMystery201404Text": "Antenas de mariposa crepuscular",
"headAccessoryMystery201404Notes": "¡Esta antena ayuda a su portador a sentir distracciones peligrosas! No confiere beneficio. Artículo de Suscriptor Abril 2014.",
@@ -1705,38 +1705,38 @@
"eyewearCapitalized": "Gafas",
"eyewearBase0Text": "Sin Gafas",
"eyewearBase0Notes": "Sin Gafas.",
"eyewearSpecialBlackTopFrameText": "Gafas simples negras",
"eyewearSpecialBlackTopFrameNotes": "Gafas con un marco negro sobre las lentes. No confieren beneficios.",
"eyewearSpecialBlueTopFrameText": "Gafas simples azules",
"eyewearSpecialBlueTopFrameNotes": "Gafas con un marco azul sobre las lentes. No confieren beneficios.",
"eyewearSpecialGreenTopFrameText": "Gafas simples verdes",
"eyewearSpecialGreenTopFrameNotes": "Gafas con un marco verde sobre las lentes. No confieren beneficios.",
"eyewearSpecialPinkTopFrameText": "Gafas simples rosas",
"eyewearSpecialPinkTopFrameNotes": "Gafas con un marco rosa sobre las lentes. No confieren beneficios.",
"eyewearSpecialRedTopFrameText": "Gafas simples rojas",
"eyewearSpecialRedTopFrameNotes": "Gafas con un marco rojo sobre las lentes. No confieren beneficios.",
"eyewearSpecialWhiteTopFrameText": "Gafas simples blancas",
"eyewearSpecialWhiteTopFrameNotes": "Gafas con un marco blanco sobre las lentes. No confieren beneficios.",
"eyewearSpecialYellowTopFrameText": "Gafas simples amarillas",
"eyewearSpecialYellowTopFrameNotes": "Gafas con un marco amarillo sobre las lentes. No confieren beneficios.",
"eyewearSpecialBlackTopFrameText": "Gafas Simples Negras",
"eyewearSpecialBlackTopFrameNotes": "Gafas con un marco negro sobre las lentes. No otorga ningún beneficio.",
"eyewearSpecialBlueTopFrameText": "Gafas Simples Azules",
"eyewearSpecialBlueTopFrameNotes": "Gafas con un marco azul sobre las lentes. No otorga ningún beneficio.",
"eyewearSpecialGreenTopFrameText": "Gafas Simples Verdes",
"eyewearSpecialGreenTopFrameNotes": "Gafas con un marco verde sobre las lentes. No otorga ningún beneficio.",
"eyewearSpecialPinkTopFrameText": "Gafas Simples Rosas",
"eyewearSpecialPinkTopFrameNotes": "Gafas con un marco rosa sobre las lentes. No otorga ningún beneficio.",
"eyewearSpecialRedTopFrameText": "Gafas Simples Rojas",
"eyewearSpecialRedTopFrameNotes": "Gafas con un marco rojo sobre las lentes. No otorga ningún beneficio.",
"eyewearSpecialWhiteTopFrameText": "Gafas Simples Blancas",
"eyewearSpecialWhiteTopFrameNotes": "Gafas con un marco blanco sobre las lentes. No otorga ningún beneficio.",
"eyewearSpecialYellowTopFrameText": "Gafas Simples Amarillas",
"eyewearSpecialYellowTopFrameNotes": "Gafas con un marco amarillo sobre las lentes. No otorga ningún beneficio.",
"eyewearSpecialAetherMaskText": "Máscara Etérea",
"eyewearSpecialAetherMaskNotes": "Esta máscara tiene un misterioso pasado. Aumenta la inteligencia en <%= int %>.",
"eyewearSpecialAetherMaskNotes": "Esta máscara tiene un misterioso pasado. Aumenta la Inteligencia en <%= int %>.",
"eyewearSpecialSummerRogueText": "Parche Picaresco",
"eyewearSpecialSummerRogueNotes": "¡No hace falta ser un diablillo para apreciar lo distinguido que es! No otorga ningún beneficio. Equipo de Verano 2014 Edición Limitada.",
"eyewearSpecialSummerRogueNotes": "¡No hace falta ser un diablillo para apreciar lo distinguido que es! No otorga ningún beneficio. Equipamiento de edición limitada de verano 2014.",
"eyewearSpecialSummerWarriorText": "Parche Apuesto",
"eyewearSpecialSummerWarriorNotes": "¡No hace falta ser un diablillo para apreciar lo distinguido que es esto! No otorga ningún beneficio. Equipamiento de Verano 2014 Edición Limitada.",
"eyewearSpecialSummerWarriorNotes": "¡No hace falta ser un diablillo para apreciar lo distinguido que es esto! No otorga ningún beneficio. Equipamiento de edición limitada de verano 2014.",
"eyewearSpecialWonderconRedText": "Máscara Poderosa",
"eyewearSpecialWonderconRedNotes": "¡Qué accesorio tan poderoso para el rostro! No otorga ningún beneficio. Artículo de Congreso Edición Especial.",
"eyewearSpecialWonderconRedNotes": "¡Qué accesorio tan poderoso para el rostro! No otorga ningún beneficio. Artículo de edición especial de convención.",
"eyewearSpecialWonderconBlackText": "Máscara Sigilosa",
"eyewearSpecialWonderconBlackNotes": "Tus motivos son indudablemente legítimos. No otorga ningún beneficio. Artículo de Congreso Edición Especial.",
"eyewearMystery201503Text": "Gafas de aguamarina",
"eyewearSpecialWonderconBlackNotes": "Tus motivos son indudablemente legítimos. No otorga ningún beneficio. Artículo de edición especial de convención.",
"eyewearMystery201503Text": "Gafas de Aguamarina",
"eyewearMystery201503Notes": "¡Cuidado no te metas estas gemas brillantes en un ojo! No otorga ningún beneficio. Artículo de suscriptor marzo 2015.",
"eyewearMystery201506Text": "Gafas de buceo neón",
"eyewearMystery201506Notes": "Con estas gafas neón para bucear, se ve debajo del agua. No aportan ningún beneficio. Artículo del suscriptor de junio del 2015.",
"eyewearMystery201507Text": "Gafas de sol guays",
"eyewearMystery201507Notes": "Con estas gafas de sol, molarás aunque el tiempo no mole. No aportan ningún beneficio. Artículo del suscriptor de julio del 2015.",
"eyewearMystery201701Text": "Gafas de sol atemporales",
"eyewearMystery201701Notes": "¡Estas gafas de sol protegerán tus ojos de los rayos más dañinos y te verás elegante sin importar en qué momento del tiempo te encuentres! No proporciona ventajas. Artículo de Suscriptor de enero de 2017.",
"eyewearMystery201506Text": "Gafas de Buceo Neón",
"eyewearMystery201506Notes": "Con estas gafas neón para bucear, se ve debajo del agua. No otorga ningún beneficio. Artículo del suscriptor de junio del 2015.",
"eyewearMystery201507Text": "Gafas de Sol Guays",
"eyewearMystery201507Notes": "Con estas gafas de sol, molarás aunque el tiempo no mole. No otorga ningún beneficio. Artículo del suscriptor de julio del 2015.",
"eyewearMystery201701Text": "Gafas de Sol Atemporales",
"eyewearMystery201701Notes": "¡Estas gafas de sol protegerán tus ojos de los rayos más dañinos y te verás elegante sin importar en qué momento del tiempo te encuentres! No otorga ningún beneficio. Artículo de Suscriptor de enero de 2017.",
"eyewearMystery301404Text": "Gafas Protectoras para los ojos",
"eyewearMystery301404Notes": "No hay ningún accesorio facial tan sofisticado como unos buenos anteojos - excepto a lo mejor, un monóculo. No otorga ningún beneficio. Artículo de Suscriptor de Abril del 2015.",
"eyewearMystery301405Text": "Monóculo",
@@ -1819,7 +1819,7 @@
"weaponMystery201911Text": "Bastón de cristal encantado",
"weaponArmoireFloridFanNotes": "Te encariñan los pliegues de seda cuando no están en uso. Incrementa la Constitución en <%= con %>. Armario Encantado: Objeto Independiente.",
"weaponArmoireFloridFanText": "Abanico Florido",
"eyewearMystery201907Notes": "¡Luce asombroso mientras proteges tus ojos de los dañinos rayos UV! No confiere beneficios. Equipamiento de suscripción de julio del 2019.",
"eyewearMystery201907Notes": "¡Luce asombroso mientras proteges tus ojos de los dañinos rayos UV! No otorga ningún beneficio. Equipamiento de suscripción de julio del 2019.",
"weaponSpecialWinter2020WarriorNotes": "¡Atrás, ardillas! ¡No os llevaréis ni un poco de esto! ... Pero si todas vosotras queréis pasar el rato y tomar chocolate caliente, está guay. Aumenta la Fuerza en <%= str %>. Equipamiento de edición limitada de invierno 2019-2020.",
"weaponSpecialWinter2020RogueText": "Varilla de Linterna",
"shieldSpecialPiDayText": "Escudo Pi",
@@ -2361,7 +2361,7 @@
"backMystery202009Notes": "¡Deja que estas enormes alas te lleven a nuevas alturas! No otorga ningún beneficio. Artículo de suscriptor de septiembre 2020.",
"bodyMystery202107Text": "Exuberante flamenco flotante",
"eyewearMystery202108Notes": "Mira con ellos fijamente a tus enemigos (¡o a tus tareas más poderosas!) y no tendrán ninguna oportunidad. No otorga ningún beneficio. Artículo de suscriptor de agosto 2021.",
"eyewearMystery201907Text": "Gafas de sol azucaradas",
"eyewearMystery201907Text": "Gafas de Sol Azucaradas",
"headAccessoryMystery202009Notes": "Estos plumosos apéndices te ayudarán a encontrar tu camino incluso en la oscuridad de la noche. No otorga ningún beneficio. Artículo de suscriptor de septiembre 2020.",
"shieldArmoireBouncyBubblesNotes": "¡Completa tu relajante baño con estas exuberantes burbujas! Aumenta la fuerza en <%= str %>. Armario Encantado: conjunto de baño de burbujas (artículo 4 de 4).",
"bodyArmoireClownsBowtieNotes": "Una buena pajarita no es ninguna broma, incluso para un payaso. Aumenta la fuerza, inteligencia, constitución y percepción en <%= attrs %> cada uno. Armario Encantado: conjunto de payaso (artículo 5 de 5).",
@@ -2375,12 +2375,12 @@
"headAccessoryMystery201906Notes": "Cuenta la leyenda, que estas pequeñas orejas permiten a los tritones a escuchar las llamadas y canciones de todos los habitantes de las profundidades. No otorga ningún beneficio. Artículo de suscriptor de junio 2019.",
"bodyMystery202008Text": "Manto de buhoráculo",
"eyewearSpecialKS2019Notes": "Audaces como un grifo... hmm, los hipogrifos no llevan viseras. Eso te recuerda que... báh, a quién intentas engañar, ¡las llevas porque molan! No otorga ningún beneficio.",
"eyewearSpecialKS2019Text": "Visor de hipogrifo legendario",
"eyewearSpecialKS2019Text": "Visor de Hipogrifo Legendario",
"shieldArmoireBagpipesNotes": "Los más envidiosos dirán que estás planeas despertar a los muertos con tus gaitas, ¡pero tú sabes que lo haces para llevar tu grupo al éxito! Aumenta la fuerza en <%= str %>. Armario Encantado: conjunto de gaitero (artículo 3 de 3).",
"backMystery202009Text": "Maravillosas alas de polilla",
"backMystery202005Text": "Maravillosas alas de wyverno",
"headAccessoryMystery202105Text": "Cuernos de dragón nebular",
"eyewearSpecialGreenHalfMoonText": "Gafas de medialuna verdes",
"eyewearSpecialGreenHalfMoonText": "Gafas de Medialuna Verdes",
"eyewearSpecialYellowHalfMoonNotes": "Gafas de montura amarilla y lentes de luna creciente. No otorga ningún beneficio.",
"shieldArmoirePiratesCompanionText": "Compañero pirata",
"backMystery202109Notes": "Deslízate suavemente por el aire del crepúsculo sin hacer ruido. No otorga ningún beneficio. Artículo de suscriptor de septiembre 2021.",
@@ -2389,7 +2389,7 @@
"bodyMystery202003Text": "Espaldares espinosos",
"shieldArmoireHeraldsMessageScrollText": "Pergamino de anuncios heráldico",
"shieldArmoireBlueMoonSaiText": "Sai de brillo lunar",
"eyewearSpecialFall2019RogueText": "Media-máscara de blancohueso",
"eyewearSpecialFall2019RogueText": "Media-Máscara de Blancohueso",
"shieldArmoireBlueCottonCandyFoodNotes": "Una dulce golosina para las mascotas más golosas. ¿A cuál le gustará más? Aumenta la constitución en <%= con %>. Armario Encantado: conjunto de comida para mascotas (artículo 9 de 10).",
"shieldArmoireSoftPinkPillowText": "Almohada rosa pálido",
"shieldArmoireHobbyHorseText": "Caballo de juguete",
@@ -2403,7 +2403,7 @@
"shieldArmoireMilkFoodNotes": "Existen muchos refranes sobre los beneficios para la salud de la leche, pero a las mascotas únicamente les gusta por su cremoso sabor. Aumenta la constitución y la fuerza en <%= attrs %> cada uno. Armario Encantado: conjunto de comida para mascotas (artículo 10 de 10)",
"shieldArmoireDarkAutumnFlameText": "Llama oscura otoñal",
"shieldArmoirePotatoFoodNotes": "Las patatas son el alimento básico de muchas dietas, pero algo algunas mascotas viven exclusivamente de ello... Aumenta la percepción en <%= per %>. Armario Encantado: conjunto de comida para mascotas (artículo 3 de 10).",
"eyewearSpecialBlackHalfMoonText": "Gafas de medialuna negras",
"eyewearSpecialBlackHalfMoonText": "Gafas de Medialuna Negras",
"shieldArmoireHoneyFoodNotes": "¡Cuidado con las patas pegajosas cuando hayas alimentado a tus mascotas con esta miel! Algunas mascotas anhelan esta dulzura natural; ¿puedes adivinar cuáles? Aumenta la percepción y la inteligencia en <%= attrs %> cada uno. Armario Encantado: conjunto de comida para mascotas (artículo 6 de 10).",
"bodyArmoireLifeguardWhistleNotes": "¡Llama al orden a ese hábito que no se está comportando!¡Debe cumplir las normas! Aumenta la inteligencia en <%= int %>. Armario Encantado: conjunto de socorrista (artículo 3 de 3).",
"headAccessoryMystery202109Text": "Antenas de lepidóptero lunar",
@@ -2414,7 +2414,7 @@
"shieldArmoirePinkCottonCandyFoodNotes": "Una golosina para las mascotas más golosas. Pero, ¿a cuál le gustará más?. Aumenta la inteligencia en <%= int %>. Armario Encantado: conjunto de comida para mascotas (artículo 4 de 10).",
"backMystery202105Text": "Alas de dragón nebular",
"backMystery202109Text": "Alas de lepidóptero lunar",
"eyewearSpecialWhiteHalfMoonText": "Gafas de medialuna blancas",
"eyewearSpecialWhiteHalfMoonText": "Gafas de Medialuna Blancas",
"shieldArmoireRottenMeatFoodNotes": "¡Tápate la nariz! Puede que a ti te resulte repugnante este trozo de carne podrida, ¡pero a algunas de tus mascotas les parece el plato perfecto! Aumenta la constitución en <%= con %>. Armario Encantado: conjunto de comida para mascotas (artículo 2 de 10).",
"bodyMystery202002Notes": "Para cuando tu corazón esté caliente pero las brisas de febrero son gélidas. No otorga ningún beneficio. Artículo de suscriptor de febrero 2020.",
"shieldArmoireChocolateFoodText": "Chocolate decorativo",
@@ -2426,7 +2426,7 @@
"shieldArmoireMortarAndPestleText": "Maja y mortero",
"bodyArmoireLifeguardWhistleText": "Silbato de socorrista",
"backMystery202005Notes": "A pesar de estar llenas de agujeros, estas alas siguen pudiendo llevarte donde necesites. No otorga ningún beneficio. Artículo de suscriptor de mayo 2020.",
"eyewearSpecialBlueHalfMoonText": "Gafas de medialuna azules",
"eyewearSpecialBlueHalfMoonText": "Gafas de Medialuna Azules",
"headAccessoryMystery202004Text": "Poderosas antenas de mariposa monarca",
"backMystery202001Notes": "¡Estas esponjosas colas contienen poder celestial y también un alto nivel de adorabilidad! No otorga ningún beneficio. Artículo de suscriptor de enero 2020.",
"eyewearSpecialPinkHalfMoonNotes": "Gafas de montura rosa y lentes de luna creciente. No otorga ningún beneficio.",
@@ -2437,17 +2437,17 @@
"backMystery201905Notes": "Vuela a parajes jamás visto con estas iridiscentes alas. No otorga ningún beneficio. Artículo de suscriptor de mayo 2019.",
"headAccessoryMystery202102Text": "Tiara encantadora",
"eyewearSpecialGreenHalfMoonNotes": "Gafas de montura verde y lentes de luna creciente. No otorga ningún beneficio.",
"eyewearSpecialPinkHalfMoonText": "Gafas de medialuna rosa",
"eyewearSpecialPinkHalfMoonText": "Gafas de Medialuna Rosa",
"shieldArmoireMedievalLaundryText": "Ropa sucia",
"shieldArmoireBlueCottonCandyFoodText": "Algodón de azúcar azul decorativo",
"backMystery202004Text": "Poderosas alas de monarca",
"eyewearSpecialYellowHalfMoonText": "Gafas de medialuna amarillas",
"eyewearSpecialYellowHalfMoonText": "Gafas de Medialuna Amarillas",
"shieldArmoireHobbyHorseNotes": "¡Cabalga tu hermoso caballo de juguete raudo hacia tus recompensas! Aumenta la percepción y la constitución en <%= attrs %> cada uno. Armario Encantado: conjunto de caballero de papel (artículo 2 de 3).",
"bodyMystery202002Text": "Bufanda de cariñito estilosa",
"headAccessoryMystery201906Text": "Orejas de tritón",
"shieldArmoireMilkFoodText": "Leche decorativa",
"shieldArmoireBouncyBubblesText": "Burbujas saltarinas",
"eyewearSpecialRedHalfMoonText": "Gafas de medialuna rojas",
"eyewearSpecialRedHalfMoonText": "Gafas de Medialuna Rojas",
"shieldArmoireBaseballGloveText": "Guante de béisbol",
"shieldArmoireStrawberryFoodNotes": "¡Una deliciosa y fresta fresa para alimentar a tus mascotas! ¿Sabes a cuáles les gustan más las fresas? Aumenta la fuerza en <%= str %>. Armario Encantado: conjunto de comida para mascotas (artículo 1 de 10).",
"shieldArmoireMedievalLaundryNotes": "Va a ser difícil aclarar esto, pero ya sabes que puedes hacer cualquier cosa. Aumenta la percepción en <%= per %>. Armario Encantado: conjunto de lavanderos medievales (artículo 6 de 6).",
@@ -2471,7 +2471,7 @@
"shieldArmoireClownsBalloonsNotes": "Ten cuidado: reemplazar estos globos si se te explotan te saldría un poco caro... ¡por la inflación! Aumenta la percepción en <%= per %>. Armario Encantado: conjunto de payaso (artículo 4 de 5).",
"shieldArmoireRottenMeatFoodText": "Carne podrida decorativa",
"headAccessoryMystery201905Text": "Temibles cuernos de dragón",
"eyewearSpecialFall2019HealerText": "Rostro negro",
"eyewearSpecialFall2019HealerText": "Rostro Negro",
"shieldArmoireHeraldsMessageScrollNotes": "¿Qué emocionantes noticias contiene este pergamino? ¿Podría tratarse de una nueva mascota o de una buena racha cumpliendo hábitos? Aumenta la percepción en <%= per %>. Armario Encantado: conjunto de heraldo (artículo 4 de 4)",
"backSpecialNamingDay2020Notes": "¡Feliz día del nombramiento! Mueve esta fiera cola pixelada mientras celebras el bautizo de Habitica. No otorga ningún beneficio.",
"bodyMystery202107Notes": "¡Este fiel compañero nunca te defraudará y siempre mantendrá tu espíritu en alto! No otorga ningún beneficio. Artículo de suscriptor de julio 2021.",
+21 -2
View File
@@ -188,7 +188,7 @@
"fall2020WraithWarriorSet": "Espectro (Guerrero)",
"royalPurpleJackolantern": "Calabaza de Halloween púrpura real",
"novemberYYYY": "Noviembre <%= year %>",
"g1g1Limitations": "Este es un evento por tiempo limitado que comienza el 17 de Diciembre a las 8:00 AM ET (13:00 UTC) y termina el 7 de Enero a las 8:00 PM ET (1:00 UTC). Esta promoción sólo es válida cuando se hace un regalo a otro Habiticano. Si tu o el receptor de tu regalo ya tenéis una suscripción, la suscripción regalada añadirá meses de crédito que sólo se usarán cuando la suscripción actual sea cancelada o expire.",
"g1g1Limitations": "Este es un evento por tiempo limitado que comienza el 15 de Diciembre a las 8:00 AM ET (13:00 UTC) y termina el 8 de Enero a las 11:59 PM ET (9 de Enero a las 04:59 UTC). Esta promoción sólo es válida cuando se hace un regalo a otro Habiticano. Si tu o el receptor de tu regalo ya tenéis una suscripción, la suscripción regalada añadirá meses de crédito que sólo se usarán cuando la suscripción actual sea cancelada o expire.",
"limitations": "Limitaciones",
"g1g1HowItWorks": "Escribe el nombre de usuario de la cuenta a la que quieres hacerle el regalo. Desde ahí, selecciona la duración de la suscripción que quieres regalar y realiza el pago. Tu cuenta será automaticamente recompensada con el mismo nivel de suscripción que acabas de regalar.",
"howItWorks": "Cómo funciona",
@@ -235,5 +235,24 @@
"gemSaleLimitations": "Esta promoción solo aplica durante el tiempo limitado del evento. Este evento empieza el <%= eventStartOrdinal %> de <%= eventStartMonth %> a las 8:00 AM EDT (12:00 UTC) y acabará el <%= eventEndOrdinal %> de <%= eventStartMonth %> a las 8:00 PM EDT (00:00 UTC). Esta promoción solo está disponible cuando se compran Gemas para uno mismo.",
"gemSaleHow": "Entre el <%= eventStartOrdinal %> y <%= eventEndOrdinal %> de <%= eventStartMonth %>, simplemente compra cualquier paquete de Gemas como normalmente, y se abonará en tu cuenta el número promocional de Gemas. ¡Más Gemas para gastar, compartir o guardar para futuras entregas!",
"fall2022KappaRogueSet": "Kapa (Pícaro)",
"fall2022WatcherHealerSet": "Mirador (Sanador)"
"fall2022WatcherHealerSet": "Mirador (Sanador)",
"winter2023WalrusWarriorSet": "Morsa (Guerrero)",
"winter2023CardinalHealerSet": "Cardenal (Sanador)",
"spring2023CaterpillarRogueSet": "Oruga (Pícaro)",
"spring2023HummingbirdWarriorSet": "Colibrí (Guerrero)",
"spring2023LilyHealerSet": "Lirio (Sanador)",
"spring2023MoonstoneMageSet": "Piedra Luna (Mago)",
"celebrateAnniversary": "¡Celebra el 10.º Cumpleaños de Habitica con los siguientes regalos y objetos exclusivos!",
"celebrateBirthday": "¡Celebra el 10.º Cumpleaños de Habitica con regalos y objetos exclusivos!",
"limitedEdition": "Equipamiento de edición limitada",
"anniversaryGryphatricePrice": "Consíguelo hoy por <strong>$9.99</strong> o <strong>60 gemas</strong>",
"dayFive": "Día 5",
"dateStartFebruary": "8 de febrero",
"anniversaryLimitedDates": "Del 30 de enero al 8 de febrero",
"limitedEvent": "Evento limitado",
"dayOne": "Día 1",
"dayTen": "Día 10",
"twentyGems": "20 Gemas",
"birthdaySet": "Conjunto de Cumpleaños",
"winter2023RibbonRogueSet": "Lazo (Pícaro)"
}
+5 -3
View File
@@ -199,14 +199,14 @@
"transaction_gift_send": "<b>Regalado</b> a",
"transaction_create_challenge": "Desafío <b>creado</b>",
"transaction_create_guild": "Gremio <b>creado</b>",
"transaction_change_class": "Clase cambiada",
"transaction_change_class": "Cambio de <b>Clase</b>",
"transaction_rebirth": "Orbe de Renacimiento usado",
"transaction_release_pets": "Mascotas soltadas",
"transaction_reroll": "Poción de Fortalecimiento usada",
"hourglassTransactions": "Transacciones de Relojes de Arena",
"transaction_gift_receive": "<b>Recibido</b> de",
"transaction_debug": "Depuración",
"transaction_contribution": "A través de contribuciones",
"transaction_contribution": "Cambio de <b>Rango</b>",
"transaction_spend": "<b>Gastado</b> en",
"transaction_release_mounts": "Monturas sueltas",
"transaction_subscription_perks": "Beneficio de la <b>suscripción</b>",
@@ -223,5 +223,7 @@
"passwordIssueLength": "Las contraseñas deben tener una longitud entre 8 y 64 caracteres.",
"amount": "Cantidad",
"action": "Acción",
"note": "Nota"
"note": "Nota",
"timestamp": "Registro de hora",
"remainingBalance": "Saldo Restante"
}

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