Merge branch 'develop' into release

This commit is contained in:
Sabe Jones 2020-04-09 06:28:06 -05:00
commit 0f2d2ddad6
73 changed files with 3861 additions and 1620 deletions

1
.gitignore vendored
View file

@ -37,6 +37,7 @@ yarn.lock
.elasticbeanstalk/* .elasticbeanstalk/*
!.elasticbeanstalk/*.cfg.yml !.elasticbeanstalk/*.cfg.yml
!.elasticbeanstalk/*.global.yml !.elasticbeanstalk/*.global.yml
/.vscode
# webstorm fake webpack for path intellisense # webstorm fake webpack for path intellisense
webpack.webstorm.config webpack.webstorm.config

View file

@ -9,3 +9,4 @@ test/**
*.swp *.swp
*.swx *.swx
website/raw_sprites/** website/raw_sprites/**
content_cache/**

View file

@ -75,5 +75,9 @@
"WEB_CONCURRENCY": 1, "WEB_CONCURRENCY": 1,
"SKIP_SSL_CHECK_KEY": "key", "SKIP_SSL_CHECK_KEY": "key",
"ENABLE_STACKDRIVER_TRACING": "false", "ENABLE_STACKDRIVER_TRACING": "false",
"APPLE_AUTH_PRIVATE_KEY": "",
"APPLE_TEAM_ID": "",
"APPLE_AUTH_CLIENT_ID": "",
"APPLE_AUTH_KEY_ID": "",
"BLOCKED_IPS": "" "BLOCKED_IPS": ""
} }

477
package-lock.json generated
View file

@ -1472,6 +1472,15 @@
"defer-to-connect": "^2.0.0" "defer-to-connect": "^2.0.0"
} }
}, },
"@types/body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
"requires": {
"@types/connect": "*",
"@types/node": "*"
}
},
"@types/cacheable-request": { "@types/cacheable-request": {
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz",
@ -1489,11 +1498,56 @@
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
"dev": true "dev": true
}, },
"@types/connect": {
"version": "3.4.33",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
"integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
"requires": {
"@types/node": "*"
}
},
"@types/events": { "@types/events": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
"integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==" "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g=="
}, },
"@types/express": {
"version": "4.17.4",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.4.tgz",
"integrity": "sha512-DO1L53rGqIDUEvOjJKmbMEQ5Z+BM2cIEPy/eV3En+s166Gz+FeuzRerxcab757u/U4v4XF4RYrZPmqKa+aY/2w==",
"requires": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "*",
"@types/qs": "*",
"@types/serve-static": "*"
}
},
"@types/express-jwt": {
"version": "0.0.42",
"resolved": "https://registry.npmjs.org/@types/express-jwt/-/express-jwt-0.0.42.tgz",
"integrity": "sha512-WszgUddvM1t5dPpJ3LhWNH8kfNN8GPIBrAGxgIYXVCEGx6Bx4A036aAuf/r5WH9DIEdlmp7gHOYvSM6U87B0ag==",
"requires": {
"@types/express": "*",
"@types/express-unless": "*"
}
},
"@types/express-serve-static-core": {
"version": "4.17.3",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.3.tgz",
"integrity": "sha512-sHEsvEzjqN+zLbqP+8OXTipc10yH1QLR+hnr5uw29gi9AhCAAAdri8ClNV7iMdrJrIzXIQtlkPvq8tJGhj3QJQ==",
"requires": {
"@types/node": "*",
"@types/range-parser": "*"
}
},
"@types/express-unless": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.5.1.tgz",
"integrity": "sha512-5fuvg7C69lemNgl0+v+CUxDYWVPSfXHhJPst4yTLcqi4zKJpORCxnDrnnilk3k0DTq/WrAUdvXFs01+vUqUZHw==",
"requires": {
"@types/express": "*"
}
},
"@types/form-data": { "@types/form-data": {
"version": "2.5.0", "version": "2.5.0",
"resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.5.0.tgz", "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.5.0.tgz",
@ -1533,6 +1587,11 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/mime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz",
"integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw=="
},
"@types/minimatch": { "@types/minimatch": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@ -1570,6 +1629,16 @@
"integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==", "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==",
"optional": true "optional": true
}, },
"@types/qs": {
"version": "6.9.1",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.1.tgz",
"integrity": "sha512-lhbQXx9HKZAPgBkISrBcmAcMpZsmpe/Cd/hY7LGZS5OfkySUBItnPZHgQPssWYUET8elF+yCFBbP1Q0RZPTdaw=="
},
"@types/range-parser": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
},
"@types/responselike": { "@types/responselike": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
@ -1583,6 +1652,15 @@
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="
}, },
"@types/serve-static": {
"version": "1.13.3",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz",
"integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==",
"requires": {
"@types/express-serve-static-core": "*",
"@types/mime": "*"
}
},
"@types/ws": { "@types/ws": {
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-5.1.2.tgz", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-5.1.2.tgz",
@ -2029,6 +2107,16 @@
"default-require-extensions": "^1.0.0" "default-require-extensions": "^1.0.0"
} }
}, },
"apple-auth": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/apple-auth/-/apple-auth-1.0.5.tgz",
"integrity": "sha512-EbMm0rqWyUANxHJfiNGrvLkS3acdBfAj0TJ1hnA/jxq7+l9GTd2br5ajgjBjcHT5bbN6CsvuZFAdYofzn1J/6A==",
"requires": {
"axios": "^0.19.0",
"express": "^4.17.1",
"jsonwebtoken": "^8.5.1"
}
},
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
@ -2298,9 +2386,9 @@
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
}, },
"aws-sdk": { "aws-sdk": {
"version": "2.648.0", "version": "2.653.0",
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.648.0.tgz", "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.653.0.tgz",
"integrity": "sha512-b+PdZmCFvZBisqXEH68jO4xB30LrDHQMWrEX6MJoZaOlxPJfpOqRFUH3zsiAXF5Q2jTdjYLtS5bs3vcIwRzi3Q==", "integrity": "sha512-vtpHfoAKoudNa5kknUgQeXzdnmkI63hqKYHuk5u7mx0HelP8iybTxmKfKENlOvkfKtBdCEbcmJRa3DxZUbQPHQ==",
"requires": { "requires": {
"buffer": "4.9.1", "buffer": "4.9.1",
"events": "1.1.1", "events": "1.1.1",
@ -2353,7 +2441,6 @@
"version": "0.19.2", "version": "0.19.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
"integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
"dev": true,
"requires": { "requires": {
"follow-redirects": "1.5.10" "follow-redirects": "1.5.10"
} }
@ -2938,6 +3025,11 @@
"safe-buffer": "^5.1.1" "safe-buffer": "^5.1.1"
} }
}, },
"bluebird": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
"integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
},
"body-parser": { "body-parser": {
"version": "1.19.0", "version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
@ -6051,22 +6143,26 @@
"dependencies": { "dependencies": {
"abbrev": { "abbrev": {
"version": "1.1.1", "version": "1.1.1",
"bundled": true, "resolved": false,
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"optional": true "optional": true
}, },
"ansi-regex": { "ansi-regex": {
"version": "2.1.1", "version": "2.1.1",
"bundled": true, "resolved": false,
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"optional": true "optional": true
}, },
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
"bundled": true, "resolved": false,
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
"optional": true "optional": true
}, },
"are-we-there-yet": { "are-we-there-yet": {
"version": "1.1.5", "version": "1.1.5",
"bundled": true, "resolved": false,
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
"optional": true, "optional": true,
"requires": { "requires": {
"delegates": "^1.0.0", "delegates": "^1.0.0",
@ -6075,12 +6171,14 @@
}, },
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "resolved": false,
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"optional": true "optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"bundled": true, "resolved": false,
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"optional": true, "optional": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
@ -6089,32 +6187,38 @@
}, },
"chownr": { "chownr": {
"version": "1.1.1", "version": "1.1.1",
"bundled": true, "resolved": false,
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
"optional": true "optional": true
}, },
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "resolved": false,
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"optional": true "optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"bundled": true, "resolved": false,
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"optional": true "optional": true
}, },
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "resolved": false,
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"optional": true "optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "resolved": false,
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"optional": true "optional": true
}, },
"debug": { "debug": {
"version": "4.1.1", "version": "4.1.1",
"bundled": true, "resolved": false,
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"optional": true, "optional": true,
"requires": { "requires": {
"ms": "^2.1.1" "ms": "^2.1.1"
@ -6122,22 +6226,26 @@
}, },
"deep-extend": { "deep-extend": {
"version": "0.6.0", "version": "0.6.0",
"bundled": true, "resolved": false,
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"optional": true "optional": true
}, },
"delegates": { "delegates": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "resolved": false,
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
"optional": true "optional": true
}, },
"detect-libc": { "detect-libc": {
"version": "1.0.3", "version": "1.0.3",
"bundled": true, "resolved": false,
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
"optional": true "optional": true
}, },
"fs-minipass": { "fs-minipass": {
"version": "1.2.5", "version": "1.2.5",
"bundled": true, "resolved": false,
"integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
"optional": true, "optional": true,
"requires": { "requires": {
"minipass": "^2.2.1" "minipass": "^2.2.1"
@ -6145,12 +6253,14 @@
}, },
"fs.realpath": { "fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "resolved": false,
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"optional": true "optional": true
}, },
"gauge": { "gauge": {
"version": "2.7.4", "version": "2.7.4",
"bundled": true, "resolved": false,
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"optional": true, "optional": true,
"requires": { "requires": {
"aproba": "^1.0.3", "aproba": "^1.0.3",
@ -6165,7 +6275,8 @@
}, },
"glob": { "glob": {
"version": "7.1.3", "version": "7.1.3",
"bundled": true, "resolved": false,
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"optional": true, "optional": true,
"requires": { "requires": {
"fs.realpath": "^1.0.0", "fs.realpath": "^1.0.0",
@ -6178,12 +6289,14 @@
}, },
"has-unicode": { "has-unicode": {
"version": "2.0.1", "version": "2.0.1",
"bundled": true, "resolved": false,
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
"optional": true "optional": true
}, },
"iconv-lite": { "iconv-lite": {
"version": "0.4.24", "version": "0.4.24",
"bundled": true, "resolved": false,
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"optional": true, "optional": true,
"requires": { "requires": {
"safer-buffer": ">= 2.1.2 < 3" "safer-buffer": ">= 2.1.2 < 3"
@ -6191,7 +6304,8 @@
}, },
"ignore-walk": { "ignore-walk": {
"version": "3.0.1", "version": "3.0.1",
"bundled": true, "resolved": false,
"integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
"optional": true, "optional": true,
"requires": { "requires": {
"minimatch": "^3.0.4" "minimatch": "^3.0.4"
@ -6199,7 +6313,8 @@
}, },
"inflight": { "inflight": {
"version": "1.0.6", "version": "1.0.6",
"bundled": true, "resolved": false,
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"optional": true, "optional": true,
"requires": { "requires": {
"once": "^1.3.0", "once": "^1.3.0",
@ -6208,17 +6323,20 @@
}, },
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.3",
"bundled": true, "resolved": false,
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"optional": true "optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
"bundled": true, "resolved": false,
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"optional": true "optional": true
}, },
"is-fullwidth-code-point": { "is-fullwidth-code-point": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "resolved": false,
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"optional": true, "optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
@ -6226,12 +6344,14 @@
}, },
"isarray": { "isarray": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "resolved": false,
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"optional": true "optional": true
}, },
"minimatch": { "minimatch": {
"version": "3.0.4", "version": "3.0.4",
"bundled": true, "resolved": false,
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"optional": true, "optional": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
@ -6239,12 +6359,14 @@
}, },
"minimist": { "minimist": {
"version": "0.0.8", "version": "0.0.8",
"bundled": true, "resolved": false,
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"optional": true "optional": true
}, },
"minipass": { "minipass": {
"version": "2.3.5", "version": "2.3.5",
"bundled": true, "resolved": false,
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
"optional": true, "optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
@ -6253,7 +6375,8 @@
}, },
"minizlib": { "minizlib": {
"version": "1.2.1", "version": "1.2.1",
"bundled": true, "resolved": false,
"integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
"optional": true, "optional": true,
"requires": { "requires": {
"minipass": "^2.2.1" "minipass": "^2.2.1"
@ -6261,7 +6384,8 @@
}, },
"mkdirp": { "mkdirp": {
"version": "0.5.1", "version": "0.5.1",
"bundled": true, "resolved": false,
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"optional": true, "optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
@ -6269,12 +6393,14 @@
}, },
"ms": { "ms": {
"version": "2.1.1", "version": "2.1.1",
"bundled": true, "resolved": false,
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"optional": true "optional": true
}, },
"needle": { "needle": {
"version": "2.3.0", "version": "2.3.0",
"bundled": true, "resolved": false,
"integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==",
"optional": true, "optional": true,
"requires": { "requires": {
"debug": "^4.1.0", "debug": "^4.1.0",
@ -6284,7 +6410,8 @@
}, },
"node-pre-gyp": { "node-pre-gyp": {
"version": "0.12.0", "version": "0.12.0",
"bundled": true, "resolved": false,
"integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==",
"optional": true, "optional": true,
"requires": { "requires": {
"detect-libc": "^1.0.2", "detect-libc": "^1.0.2",
@ -6301,7 +6428,8 @@
}, },
"nopt": { "nopt": {
"version": "4.0.1", "version": "4.0.1",
"bundled": true, "resolved": false,
"integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
"optional": true, "optional": true,
"requires": { "requires": {
"abbrev": "1", "abbrev": "1",
@ -6310,12 +6438,14 @@
}, },
"npm-bundled": { "npm-bundled": {
"version": "1.0.6", "version": "1.0.6",
"bundled": true, "resolved": false,
"integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==",
"optional": true "optional": true
}, },
"npm-packlist": { "npm-packlist": {
"version": "1.4.1", "version": "1.4.1",
"bundled": true, "resolved": false,
"integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==",
"optional": true, "optional": true,
"requires": { "requires": {
"ignore-walk": "^3.0.1", "ignore-walk": "^3.0.1",
@ -6324,7 +6454,8 @@
}, },
"npmlog": { "npmlog": {
"version": "4.1.2", "version": "4.1.2",
"bundled": true, "resolved": false,
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"optional": true, "optional": true,
"requires": { "requires": {
"are-we-there-yet": "~1.1.2", "are-we-there-yet": "~1.1.2",
@ -6335,17 +6466,20 @@
}, },
"number-is-nan": { "number-is-nan": {
"version": "1.0.1", "version": "1.0.1",
"bundled": true, "resolved": false,
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"optional": true "optional": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
"bundled": true, "resolved": false,
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"optional": true "optional": true
}, },
"once": { "once": {
"version": "1.4.0", "version": "1.4.0",
"bundled": true, "resolved": false,
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"optional": true, "optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
@ -6353,17 +6487,20 @@
}, },
"os-homedir": { "os-homedir": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "resolved": false,
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
"optional": true "optional": true
}, },
"os-tmpdir": { "os-tmpdir": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "resolved": false,
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"optional": true "optional": true
}, },
"osenv": { "osenv": {
"version": "0.1.5", "version": "0.1.5",
"bundled": true, "resolved": false,
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"optional": true, "optional": true,
"requires": { "requires": {
"os-homedir": "^1.0.0", "os-homedir": "^1.0.0",
@ -6372,17 +6509,20 @@
}, },
"path-is-absolute": { "path-is-absolute": {
"version": "1.0.1", "version": "1.0.1",
"bundled": true, "resolved": false,
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"optional": true "optional": true
}, },
"process-nextick-args": { "process-nextick-args": {
"version": "2.0.0", "version": "2.0.0",
"bundled": true, "resolved": false,
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
"optional": true "optional": true
}, },
"rc": { "rc": {
"version": "1.2.8", "version": "1.2.8",
"bundled": true, "resolved": false,
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"optional": true, "optional": true,
"requires": { "requires": {
"deep-extend": "^0.6.0", "deep-extend": "^0.6.0",
@ -6393,14 +6533,16 @@
"dependencies": { "dependencies": {
"minimist": { "minimist": {
"version": "1.2.0", "version": "1.2.0",
"bundled": true, "resolved": false,
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"optional": true "optional": true
} }
} }
}, },
"readable-stream": { "readable-stream": {
"version": "2.3.6", "version": "2.3.6",
"bundled": true, "resolved": false,
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"optional": true, "optional": true,
"requires": { "requires": {
"core-util-is": "~1.0.0", "core-util-is": "~1.0.0",
@ -6414,7 +6556,8 @@
}, },
"rimraf": { "rimraf": {
"version": "2.6.3", "version": "2.6.3",
"bundled": true, "resolved": false,
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"optional": true, "optional": true,
"requires": { "requires": {
"glob": "^7.1.3" "glob": "^7.1.3"
@ -6422,37 +6565,44 @@
}, },
"safe-buffer": { "safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"bundled": true, "resolved": false,
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"optional": true "optional": true
}, },
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
"bundled": true, "resolved": false,
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"optional": true "optional": true
}, },
"sax": { "sax": {
"version": "1.2.4", "version": "1.2.4",
"bundled": true, "resolved": false,
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"optional": true "optional": true
}, },
"semver": { "semver": {
"version": "5.7.0", "version": "5.7.0",
"bundled": true, "resolved": false,
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
"optional": true "optional": true
}, },
"set-blocking": { "set-blocking": {
"version": "2.0.0", "version": "2.0.0",
"bundled": true, "resolved": false,
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
"optional": true "optional": true
}, },
"signal-exit": { "signal-exit": {
"version": "3.0.2", "version": "3.0.2",
"bundled": true, "resolved": false,
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"optional": true "optional": true
}, },
"string-width": { "string-width": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "resolved": false,
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"optional": true, "optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
@ -6462,7 +6612,8 @@
}, },
"string_decoder": { "string_decoder": {
"version": "1.1.1", "version": "1.1.1",
"bundled": true, "resolved": false,
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"optional": true, "optional": true,
"requires": { "requires": {
"safe-buffer": "~5.1.0" "safe-buffer": "~5.1.0"
@ -6470,7 +6621,8 @@
}, },
"strip-ansi": { "strip-ansi": {
"version": "3.0.1", "version": "3.0.1",
"bundled": true, "resolved": false,
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"optional": true, "optional": true,
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-regex": "^2.0.0"
@ -6478,12 +6630,14 @@
}, },
"strip-json-comments": { "strip-json-comments": {
"version": "2.0.1", "version": "2.0.1",
"bundled": true, "resolved": false,
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"optional": true "optional": true
}, },
"tar": { "tar": {
"version": "4.4.8", "version": "4.4.8",
"bundled": true, "resolved": false,
"integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
"optional": true, "optional": true,
"requires": { "requires": {
"chownr": "^1.1.1", "chownr": "^1.1.1",
@ -6497,12 +6651,14 @@
}, },
"util-deprecate": { "util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "resolved": false,
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"optional": true "optional": true
}, },
"wide-align": { "wide-align": {
"version": "1.1.3", "version": "1.1.3",
"bundled": true, "resolved": false,
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
"optional": true, "optional": true,
"requires": { "requires": {
"string-width": "^1.0.2 || 2" "string-width": "^1.0.2 || 2"
@ -6510,12 +6666,14 @@
}, },
"wrappy": { "wrappy": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "resolved": false,
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"optional": true "optional": true
}, },
"yallist": { "yallist": {
"version": "3.0.3", "version": "3.0.3",
"bundled": true, "resolved": false,
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
"optional": true "optional": true
} }
} }
@ -8653,6 +8811,125 @@
"safe-buffer": "^5.0.1" "safe-buffer": "^5.0.1"
} }
}, },
"jwks-rsa": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-1.7.0.tgz",
"integrity": "sha512-tq7DVJt9J6wTvl9+AQfwZIiPSuY2Vf0F+MovfRTFuBqLB1xgDVhegD33ChEAQ6yBv9zFvUIyj4aiwrSA5VehUw==",
"requires": {
"@types/express-jwt": "0.0.42",
"debug": "^4.1.0",
"jsonwebtoken": "^8.5.1",
"limiter": "^1.1.4",
"lru-memoizer": "^2.0.1",
"ms": "^2.1.2",
"request": "^2.88.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
},
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
},
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
},
"form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
}
},
"har-validator": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
"integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
"requires": {
"ajv": "^6.5.5",
"har-schema": "^2.0.0"
}
},
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"requires": {
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
"sshpk": "^1.7.0"
}
},
"oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
"request": {
"version": "2.88.2",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
"requires": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
"caseless": "~0.12.0",
"combined-stream": "~1.0.6",
"extend": "~3.0.2",
"forever-agent": "~0.6.1",
"form-data": "~2.3.2",
"har-validator": "~5.1.3",
"http-signature": "~1.2.0",
"is-typedarray": "~1.0.0",
"isstream": "~0.1.2",
"json-stringify-safe": "~5.0.1",
"mime-types": "~2.1.19",
"oauth-sign": "~0.9.0",
"performance-now": "^2.1.0",
"qs": "~6.5.2",
"safe-buffer": "^5.1.2",
"tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
}
},
"tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
"requires": {
"psl": "^1.1.28",
"punycode": "^2.1.1"
}
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"requires": {
"safe-buffer": "^5.0.1"
}
}
}
},
"jws": { "jws": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
@ -8802,6 +9079,11 @@
"resolve": "^1.1.7" "resolve": "^1.1.7"
} }
}, },
"limiter": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz",
"integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA=="
},
"linkify-it": { "linkify-it": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
@ -8842,6 +9124,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
}, },
"lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
},
"lodash.includes": { "lodash.includes": {
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
@ -8978,6 +9265,31 @@
"yallist": "^3.0.2" "yallist": "^3.0.2"
} }
}, },
"lru-memoizer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.1.2.tgz",
"integrity": "sha512-N5L5xlnVcbIinNn/TJ17vHBZwBMt9t7aJDz2n97moWubjNl6VO9Ao2XuAGBBddkYdjrwR9HfzXbT6NfMZXAZ/A==",
"requires": {
"lodash.clonedeep": "^4.5.0",
"lru-cache": "~4.0.0"
},
"dependencies": {
"lru-cache": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz",
"integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4=",
"requires": {
"pseudomap": "^1.0.1",
"yallist": "^2.0.0"
}
},
"yallist": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
}
}
},
"make-dir": { "make-dir": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
@ -9456,9 +9768,9 @@
} }
}, },
"mongoose": { "mongoose": {
"version": "5.9.6", "version": "5.9.7",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.9.6.tgz", "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.9.7.tgz",
"integrity": "sha512-EfFGO2QUoenf/4eFeF5y2R8aBLKHtqwrMk1pVGgl3OyNWufP5XLLPIuihP006YqR1+6xM1YsBzGpgBjMZkINGA==", "integrity": "sha512-WJOBh9WMvivqBK8my9HFtSzSySKdUxJPNGAwswEakAasWUcPXJl3yHMtZ4ngGnKbwTT9KnAr75xamlt/PouR9w==",
"requires": { "requires": {
"bson": "~1.1.1", "bson": "~1.1.1",
"kareem": "2.3.1", "kareem": "2.3.1",
@ -9604,11 +9916,6 @@
"sliced": "1.0.1" "sliced": "1.0.1"
}, },
"dependencies": { "dependencies": {
"bluebird": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
"integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
},
"debug": { "debug": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
@ -13873,14 +14180,14 @@
} }
}, },
"winston-loggly-bulk": { "winston-loggly-bulk": {
"version": "3.0.1", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/winston-loggly-bulk/-/winston-loggly-bulk-3.0.1.tgz", "resolved": "https://registry.npmjs.org/winston-loggly-bulk/-/winston-loggly-bulk-3.1.0.tgz",
"integrity": "sha512-C86aohUze/qCRYhOVyxgqTuz3HmDK4mLss3OsVHxb4hfD+gw8jAEl3LEMOsqWHEnoJp/MC0wE1N3PeUcisLi2g==", "integrity": "sha512-W03eEzM4JIGSTwUHc50SEgWiDcYg2GWMshjqILqFeDfqHDej4f0YCr3kzDIAp/mSbAsoirGITtaQONDkYBM0qg==",
"requires": { "requires": {
"clone": "^2.1.1", "lodash.clonedeep": "^4.5.0",
"node-loggly-bulk": "^2.0.1", "node-loggly-bulk": "^2.2.4",
"winston": "^3.0", "winston": "^3.2",
"winston-transport": "^4.2.0" "winston-transport": "^4.3.0"
} }
}, },
"winston-transport": { "winston-transport": {

View file

@ -14,7 +14,8 @@
"amplitude": "^3.5.0", "amplitude": "^3.5.0",
"apidoc": "^0.17.5", "apidoc": "^0.17.5",
"apn": "^2.2.0", "apn": "^2.2.0",
"aws-sdk": "^2.648.0", "apple-auth": "^1.0.5",
"aws-sdk": "^2.653.0",
"bcrypt": "^3.0.8", "bcrypt": "^3.0.8",
"body-parser": "^1.18.3", "body-parser": "^1.18.3",
"compression": "^1.7.4", "compression": "^1.7.4",
@ -41,12 +42,14 @@
"image-size": "^0.8.3", "image-size": "^0.8.3",
"in-app-purchase": "^1.11.3", "in-app-purchase": "^1.11.3",
"js2xmlparser": "^4.0.1", "js2xmlparser": "^4.0.1",
"jsonwebtoken": "^8.5.1",
"jwks-rsa": "^1.7.0",
"lodash": "^4.17.15", "lodash": "^4.17.15",
"merge-stream": "^2.0.0", "merge-stream": "^2.0.0",
"method-override": "^3.0.0", "method-override": "^3.0.0",
"moment": "^2.24.0", "moment": "^2.24.0",
"moment-recur": "^1.0.7", "moment-recur": "^1.0.7",
"mongoose": "^5.9.6", "mongoose": "^5.9.7",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"nconf": "^0.10.0", "nconf": "^0.10.0",
"node-gcm": "^1.0.2", "node-gcm": "^1.0.2",
@ -69,7 +72,7 @@
"validator": "^11.0.0", "validator": "^11.0.0",
"vinyl-buffer": "^1.0.1", "vinyl-buffer": "^1.0.1",
"winston": "^3.2.1", "winston": "^3.2.1",
"winston-loggly-bulk": "^3.0.1", "winston-loggly-bulk": "^3.1.0",
"xml2js": "^0.4.23" "xml2js": "^0.4.23"
}, },
"private": true, "private": true,

View file

@ -18,6 +18,16 @@ function getUser () {
value: 'email@facebook', value: 'email@facebook',
}], }],
}, },
google: {
emails: [{
value: 'email@google',
}],
},
apple: {
emails: [{
value: 'email@apple',
}],
},
}, },
profile: { profile: {
name: 'profile name', name: 'profile name',
@ -58,6 +68,8 @@ describe('emails', () => {
const user = getUser(); const user = getUser();
delete user.profile.name; delete user.profile.name;
delete user.auth.local.email; delete user.auth.local.email;
delete user.auth.google.emails;
delete user.auth.apple.emails;
const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']); const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
@ -67,12 +79,48 @@ describe('emails', () => {
expect(data).to.have.property('canSend', true); expect(data).to.have.property('canSend', true);
}); });
it('returns correct user data [google users]', () => {
const attachEmail = requireAgain(pathToEmailLib);
const { getUserInfo } = attachEmail;
const user = getUser();
delete user.profile.name;
delete user.auth.local.email;
delete user.auth.facebook.emails;
delete user.auth.apple.emails;
const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
expect(data).to.have.property('name', user.auth.local.username);
expect(data).to.have.property('email', user.auth.google.emails[0].value);
expect(data).to.have.property('_id', user._id);
expect(data).to.have.property('canSend', true);
});
it('returns correct user data [apple users]', () => {
const attachEmail = requireAgain(pathToEmailLib);
const { getUserInfo } = attachEmail;
const user = getUser();
delete user.profile.name;
delete user.auth.local.email;
delete user.auth.google.emails;
delete user.auth.facebook.emails;
const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
expect(data).to.have.property('name', user.auth.local.username);
expect(data).to.have.property('email', user.auth.apple.emails[0].value);
expect(data).to.have.property('_id', user._id);
expect(data).to.have.property('canSend', true);
});
it('has fallbacks for missing data', () => { it('has fallbacks for missing data', () => {
const attachEmail = requireAgain(pathToEmailLib); const attachEmail = requireAgain(pathToEmailLib);
const { getUserInfo } = attachEmail; const { getUserInfo } = attachEmail;
const user = getUser(); const user = getUser();
delete user.auth.local.email; delete user.auth.local.email;
delete user.auth.facebook; delete user.auth.facebook;
delete user.auth.google;
delete user.auth.apple;
const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']); const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);

View file

@ -346,4 +346,23 @@ describe('DELETE /user', () => {
await expect(checkExistence('users', user._id)).to.eventually.eql(false); await expect(checkExistence('users', user._id)).to.eventually.eql(false);
}); });
}); });
context('user with Apple auth', async () => {
beforeEach(async () => {
user = await generateUser({
auth: {
apple: {
id: 'apple-id',
},
},
});
});
it('deletes a Apple user', async () => {
await user.del('/user', {
password: DELETE_CONFIRMATION,
});
await expect(checkExistence('users', user._id)).to.eventually.eql(false);
});
});
}); });

View file

@ -95,4 +95,42 @@ describe('DELETE social registration', () => {
expect(user.auth.goodl).to.be.undefined; expect(user.auth.goodl).to.be.undefined;
}); });
}); });
context('Apple', () => {
it('fails if user does not have an alternative registration method', async () => {
await user.update({
'auth.apple.id': 'some-apple-id',
'auth.local': { ok: true },
});
await expect(user.del('/user/auth/social/apple')).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('cantDetachSocial'),
});
});
it('succeeds if user has a local registration', async () => {
await user.update({
'auth.apple.id': 'some-apple-id',
});
const response = await user.del('/user/auth/social/apple');
expect(response).to.eql({});
await user.sync();
expect(user.auth.apple).to.be.undefined;
});
it('succeeds if user has a facebook registration', async () => {
await user.update({
'auth.apple.id': 'some-apple-id',
'auth.facebook.id': 'some-facebook-id',
'auth.local': { ok: true },
});
const response = await user.del('/user/auth/social/apple');
expect(response).to.eql({});
await user.sync();
expect(user.auth.goodl).to.be.undefined;
});
});
}); });

View file

@ -0,0 +1,50 @@
import {
generateUser,
requester,
getProperty,
} from '../../../../../helpers/api-integration/v3';
import * as appleAuth from '../../../../../../website/server/libs/auth/apple';
describe('GET /user/auth/apple', () => {
let api;
let user;
const appleEndpoint = '/user/auth/apple';
before(async () => {
const expectedResult = { id: 'appleId', name: 'an apple user' };
sandbox.stub(appleAuth, 'appleProfile').returns(Promise.resolve(expectedResult));
});
beforeEach(async () => {
api = requester();
user = await generateUser();
});
it('registers a new user', async () => {
const response = await api.get(appleEndpoint);
expect(response.apiToken).to.exist;
expect(response.id).to.exist;
expect(response.newUser).to.be.true;
await expect(getProperty('users', response.id, 'auth.apple.id')).to.eventually.equal('appleId');
await expect(getProperty('users', response.id, 'profile.name')).to.eventually.equal('an apple user');
});
it('logs an existing user in', async () => {
const registerResponse = await api.get(appleEndpoint);
const response = await api.get(appleEndpoint);
expect(response.apiToken).to.eql(registerResponse.apiToken);
expect(response.id).to.eql(registerResponse.id);
expect(response.newUser).to.be.false;
});
it('add social auth to an existing user', async () => {
const response = await user.get(appleEndpoint);
expect(response.apiToken).to.exist;
expect(response.id).to.exist;
expect(response.newUser).to.be.false;
});
});

View file

@ -492,6 +492,74 @@ describe('POST /user/auth/local/register', () => {
}); });
}); });
context('attach to google user', () => {
let user;
const email = 'some@email-google.net';
const username = 'some-username-google';
const password = 'some-password';
beforeEach(async () => {
user = await generateUser();
});
it('checks onlySocialAttachLocal', async () => {
await expect(user.post('/user/auth/local/register', {
email,
username,
password,
confirmPassword: password,
})).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('onlySocialAttachLocal'),
});
});
it('succeeds', async () => {
await user.update({ 'auth.google.id': 'some-google-id', 'auth.local': { ok: true } });
await user.post('/user/auth/local/register', {
username,
email,
password,
confirmPassword: password,
});
await user.sync();
expect(user.auth.local.username).to.eql(username);
expect(user.auth.local.email).to.eql(email);
});
});
context('attach to apple user', () => {
let user;
const email = 'some@email-apple.net';
const username = 'some-username-apple';
const password = 'some-password';
beforeEach(async () => {
user = await generateUser();
});
it('checks onlySocialAttachLocal', async () => {
await expect(user.post('/user/auth/local/register', {
email,
username,
password,
confirmPassword: password,
})).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('onlySocialAttachLocal'),
});
});
it('succeeds', async () => {
await user.update({ 'auth.apple.id': 'some-apple-id', 'auth.local': { ok: true } });
await user.post('/user/auth/local/register', {
username,
email,
password,
confirmPassword: password,
});
await user.sync();
expect(user.auth.local.username).to.eql(username);
expect(user.auth.local.email).to.eql(email);
});
});
context('login is already taken', () => { context('login is already taken', () => {
let username; let email; let let username; let email; let
api; api;

View file

@ -51,6 +51,7 @@ describe('POST /user/auth/social', () => {
await expect(getProperty('users', response.id, 'profile.name')).to.eventually.equal('a facebook user'); await expect(getProperty('users', response.id, 'profile.name')).to.eventually.equal('a facebook user');
await expect(getProperty('users', response.id, 'auth.local.lowerCaseUsername')).to.exist; await expect(getProperty('users', response.id, 'auth.local.lowerCaseUsername')).to.exist;
await expect(getProperty('users', response.id, 'auth.facebook.id')).to.eventually.equal(facebookId);
}); });
it('logs an existing user in', async () => { it('logs an existing user in', async () => {
@ -106,6 +107,7 @@ describe('POST /user/auth/social', () => {
expect(response.apiToken).to.exist; expect(response.apiToken).to.exist;
expect(response.id).to.exist; expect(response.id).to.exist;
expect(response.newUser).to.be.true; expect(response.newUser).to.be.true;
await expect(getProperty('users', response.id, 'auth.google.id')).to.eventually.equal(googleId);
await expect(getProperty('users', response.id, 'profile.name')).to.eventually.equal('a google user'); await expect(getProperty('users', response.id, 'profile.name')).to.eventually.equal('a google user');
}); });

View file

@ -457,6 +457,74 @@ describe('POST /user/auth/local/register', () => {
}); });
}); });
context('attach to google user', () => {
let user;
const email = 'some-google@email.net';
const username = 'some-username-google';
const password = 'some-password';
beforeEach(async () => {
user = await generateUser();
});
it('checks onlySocialAttachLocal', async () => {
await expect(user.post('/user/auth/local/register', {
email,
username,
password,
confirmPassword: password,
})).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('onlySocialAttachLocal'),
});
});
it('succeeds', async () => {
await user.update({ 'auth.google.id': 'some-google-id', 'auth.local': { ok: true } });
await user.post('/user/auth/local/register', {
username,
email,
password,
confirmPassword: password,
});
await user.sync();
expect(user.auth.local.username).to.eql(username);
expect(user.auth.local.email).to.eql(email);
});
});
context('attach to apple user', () => {
let user;
const email = 'some-apple@email.net';
const username = 'some-username-apple';
const password = 'some-password';
beforeEach(async () => {
user = await generateUser();
});
it('checks onlySocialAttachLocal', async () => {
await expect(user.post('/user/auth/local/register', {
email,
username,
password,
confirmPassword: password,
})).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('onlySocialAttachLocal'),
});
});
it('succeeds', async () => {
await user.update({ 'auth.apple.id': 'some-apple-id', 'auth.local': { ok: true } });
await user.post('/user/auth/local/register', {
username,
email,
password,
confirmPassword: password,
});
await user.sync();
expect(user.auth.local.username).to.eql(username);
expect(user.auth.local.email).to.eql(email);
});
});
context('login is already taken', () => { context('login is already taken', () => {
let username; let email; let let username; let email; let
api; api;

File diff suppressed because it is too large Load diff

View file

@ -13,23 +13,23 @@
"test:unit": "vue-cli-service test:unit --require ./tests/unit/helpers.js" "test:unit": "vue-cli-service test:unit --require ./tests/unit/helpers.js"
}, },
"dependencies": { "dependencies": {
"@vue/cli-plugin-babel": "^4.2.3", "@vue/cli-plugin-babel": "^4.3.0",
"@vue/cli-plugin-eslint": "^4.2.3", "@vue/cli-plugin-eslint": "^4.3.0",
"@vue/cli-plugin-router": "^4.2.3", "@vue/cli-plugin-router": "^4.3.0",
"@vue/cli-plugin-unit-mocha": "^4.2.3", "@vue/cli-plugin-unit-mocha": "^4.3.0",
"@vue/cli-service": "^4.2.3", "@vue/cli-service": "^4.3.0",
"@storybook/addon-actions": "^5.3.17", "@storybook/addon-actions": "^5.3.18",
"@storybook/addon-knobs": "^5.3.17", "@storybook/addon-knobs": "^5.3.18",
"@storybook/addon-links": "^5.3.17", "@storybook/addon-links": "^5.3.18",
"@storybook/addon-notes": "^5.3.17", "@storybook/addon-notes": "^5.3.18",
"@storybook/vue": "^5.3.17", "@storybook/vue": "^5.3.18",
"@vue/test-utils": "1.0.0-beta.29", "@vue/test-utils": "1.0.0-beta.29",
"amplitude-js": "^5.10.0", "amplitude-js": "^5.10.0",
"axios": "^0.19.2", "axios": "^0.19.2",
"axios-progress-bar": "^1.2.0", "axios-progress-bar": "^1.2.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"bootstrap": "^4.4.1", "bootstrap": "^4.4.1",
"bootstrap-vue": "^2.9.0", "bootstrap-vue": "^2.10.1",
"chai": "^4.1.2", "chai": "^4.1.2",
"core-js": "^3.6.4", "core-js": "^3.6.4",
"eslint": "^6.8.0", "eslint": "^6.8.0",

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="170px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 170 170" version="1.1" height="170px">
<path d="m150.37 130.25c-2.45 5.66-5.35 10.87-8.71 15.66-4.58 6.53-8.33 11.05-11.22 13.56-4.48 4.12-9.28 6.23-14.42 6.35-3.69 0-8.14-1.05-13.32-3.18-5.197-2.12-9.973-3.17-14.34-3.17-4.58 0-9.492 1.05-14.746 3.17-5.262 2.13-9.501 3.24-12.742 3.35-4.929 0.21-9.842-1.96-14.746-6.52-3.13-2.73-7.045-7.41-11.735-14.04-5.032-7.08-9.169-15.29-12.41-24.65-3.471-10.11-5.211-19.9-5.211-29.378 0-10.857 2.346-20.221 7.045-28.068 3.693-6.303 8.606-11.275 14.755-14.925s12.793-5.51 19.948-5.629c3.915 0 9.049 1.211 15.429 3.591 6.362 2.388 10.447 3.599 12.238 3.599 1.339 0 5.877-1.416 13.57-4.239 7.275-2.618 13.415-3.702 18.445-3.275 13.63 1.1 23.87 6.473 30.68 16.153-12.19 7.386-18.22 17.731-18.1 31.002 0.11 10.337 3.86 18.939 11.23 25.769 3.34 3.17 7.07 5.62 11.22 7.36-0.9 2.61-1.85 5.11-2.86 7.51zm-31.26-123.01c0 8.1021-2.96 15.667-8.86 22.669-7.12 8.324-15.732 13.134-25.071 12.375-0.119-0.972-0.188-1.995-0.188-3.07 0-7.778 3.386-16.102 9.399-22.908 3.002-3.446 6.82-6.3113 11.45-8.597 4.62-2.2516 8.99-3.4968 13.1-3.71 0.12 1.0831 0.17 2.1663 0.17 3.2409z" fill="#FFF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1000"
viewBox="0 0 1000 1187.198"
version="1.1"
height="1187.198"
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="Apple_1998.svg">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="705"
id="namedview6"
showgrid="false"
inkscape:zoom="0.1767767"
inkscape:cx="-1066.5045"
inkscape:cy="964.94669"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<path
d="m 979.04184,925.18785 c -17.95397,41.47737 -39.20563,79.65705 -63.82824,114.75895 -33.56298,47.8528 -61.04356,80.9761 -82.22194,99.3698 -32.83013,30.192 -68.00529,45.6544 -105.67203,46.5338 -27.04089,0 -59.6512,-7.6946 -97.61105,-23.3035 -38.08442,-15.5358 -73.08371,-23.2303 -105.08578,-23.2303 -33.56296,0 -69.55888,7.6945 -108.06101,23.2303 -38.5608,15.6089 -69.62484,23.7432 -93.37541,24.5493 -36.12049,1.5389 -72.1237,-14.3632 -108.06101,-47.7796 -22.93711,-20.0059 -51.62684,-54.3017 -85.99592,-102.8874 C 92.254176,984.54592 61.937588,924.38175 38.187028,855.7902 12.750995,781.70252 0,709.95986 0,640.50361 0,560.94181 17.191859,492.32094 51.626869,434.81688 78.689754,388.62753 114.69299,352.19192 159.75381,325.44413 c 45.06086,-26.74775 93.74914,-40.37812 146.18212,-41.25019 28.68971,0 66.3125,8.8744 113.06613,26.31542 46.62174,17.49964 76.55727,26.37404 89.68198,26.37404 9.8124,0 43.06758,-10.37669 99.4431,-31.06405 53.31237,-19.18512 98.30724,-27.12887 135.16787,-23.99975 99.8828,8.06098 174.92313,47.43518 224.82789,118.37174 -89.33023,54.12578 -133.51903,129.93556 -132.63966,227.18753 0.8061,75.75115 28.28668,138.78795 82.2952,188.8393 24.47603,23.23022 51.81008,41.18421 82.22186,53.93522 -6.59525,19.12648 -13.557,37.44688 -20.95846,55.03446 z M 749.96366,23.751237 c 0,59.37343 -21.69138,114.810233 -64.92748,166.121963 -52.17652,60.99961 -115.28658,96.24803 -183.72426,90.68597 -0.87204,-7.12298 -1.37769,-14.61967 -1.37769,-22.49743 0,-56.99843 24.81315,-117.99801 68.87738,-167.873453 21.99909,-25.25281 49.978,-46.25018 83.90738,-63.00018 C 686.57507,10.688027 718.59913,1.5631274 748.71783,5.2734376e-4 749.59727,7.9378274 749.96366,15.875627 749.96366,23.750467 Z"
id="path4"
inkscape:connector-curvature="0" />
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -1,7 +1,7 @@
<template> <template>
<div class="form"> <div class="form">
<div class="form-group row text-center"> <div class="form-group row text-center">
<div class="col-12 col-md-6"> <div class="col-12">
<div <div
class="btn btn-secondary social-button" class="btn btn-secondary social-button"
@click="socialAuth('facebook')" @click="socialAuth('facebook')"
@ -15,7 +15,9 @@
: $t('loginWithSocial', {social: 'Facebook'}) }}</span> : $t('loginWithSocial', {social: 'Facebook'}) }}</span>
</div> </div>
</div> </div>
<div class="col-12 col-md-6"> </div>
<div class="form-group row text-center">
<div class="col-12">
<div <div
class="btn btn-secondary social-button" class="btn btn-secondary social-button"
@click="socialAuth('google')" @click="socialAuth('google')"
@ -30,6 +32,22 @@
</div> </div>
</div> </div>
</div> </div>
<div class="form-group row text-center">
<div class="col-12">
<div
class="btn btn-secondary social-button"
@click="socialAuth('apple')"
>
<div
class="svg-icon social-icon apple-icon"
v-html="icons.appleIcon"
></div>
<span>{{ registering
? $t('signUpWithSocial', {social: 'Apple'})
: $t('loginWithSocial', {social: 'Apple'}) }}</span>
</div>
</div>
</div>
<div <div
v-if="registering" v-if="registering"
class="form-group" class="form-group"
@ -199,7 +217,11 @@
height: 18px; height: 18px;
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;
margin-top: .2em; margin-top: .1em;
}
.apple-icon {
margin-top: -1px;
} }
small.form-text { small.form-text {
@ -219,10 +241,11 @@
import hello from 'hellojs'; import hello from 'hellojs';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import isEmail from 'validator/lib/isEmail'; import isEmail from 'validator/lib/isEmail';
import { setUpAxios } from '@/libs/auth'; import { setUpAxios, buildAppleAuthUrl } from '@/libs/auth';
import { MINIMUM_PASSWORD_LENGTH } from '@/../../common/script/constants'; import { MINIMUM_PASSWORD_LENGTH } from '@/../../common/script/constants';
import facebookSquareIcon from '@/assets/svg/facebook-square.svg'; import facebookSquareIcon from '@/assets/svg/facebook-square.svg';
import googleIcon from '@/assets/svg/google.svg'; import googleIcon from '@/assets/svg/google.svg';
import appleIcon from '@/assets/svg/apple_black.svg';
export default { export default {
name: 'AuthForm', name: 'AuthForm',
@ -239,6 +262,7 @@ export default {
data.icons = Object.freeze({ data.icons = Object.freeze({
facebookIcon: facebookSquareIcon, facebookIcon: facebookSquareIcon,
googleIcon, googleIcon,
appleIcon,
}); });
return data; return data;
@ -307,27 +331,31 @@ export default {
}, 500), }, 500),
// @TODO: Abstract hello in to action or lib // @TODO: Abstract hello in to action or lib
async socialAuth (network) { async socialAuth (network) {
try { if (network === 'apple') {
await hello(network).logout(); window.location.href = buildAppleAuthUrl();
} catch (e) {} // eslint-disable-line } else {
try {
await hello(network).logout();
} catch (e) {} // eslint-disable-line
try { try {
const redirectUrl = `${window.location.protocol}//${window.location.host}`; const redirectUrl = `${window.location.protocol}//${window.location.host}`;
const auth = await hello(network).login({ const auth = await hello(network).login({
scope: 'email', scope: 'email',
redirect_uri: redirectUrl, // eslint-disable-line camelcase redirect_uri: redirectUrl, // eslint-disable-line camelcase
}); });
await this.$store.dispatch('auth:socialAuth', { await this.$store.dispatch('auth:socialAuth', {
auth, auth,
}); });
await this.finishAuth(); await this.finishAuth();
} catch (err) { } catch (err) {
console.error(err); // eslint-disable-line no-console console.error(err); // eslint-disable-line no-console
// logout the user // logout the user
await hello(network).logout(); await hello(network).logout();
this.socialAuth(network); // login again this.socialAuth(network); // login again
}
} }
}, },
async register () { async register () {

View file

@ -36,7 +36,7 @@
</div> </div>
</div> </div>
<div class="form-group row text-center"> <div class="form-group row text-center">
<div class="col-12 col-md-6"> <div class="col-12 col-md-12">
<div <div
class="btn btn-secondary social-button" class="btn btn-secondary social-button"
@click="socialAuth('facebook')" @click="socialAuth('facebook')"
@ -54,7 +54,9 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-12 col-md-6"> </div>
<div class="form-group row text-center">
<div class="col-12 col-md-12">
<div <div
class="btn btn-secondary social-button" class="btn btn-secondary social-button"
@click="socialAuth('google')" @click="socialAuth('google')"
@ -73,6 +75,29 @@
</div> </div>
</div> </div>
</div> </div>
<div class="form-group row text-center">
<div class="col-12 col-md-12">
<div
class="btn btn-secondary social-button"
@click="socialAuth('apple')"
>
<div
class="svg-icon social-icon"
v-html="icons.appleIcon"
></div>
<div
class="text"
>
{{ registering
? $t('signUpWithSocial', {social: 'Apple'})
: $t('loginWithSocial', {social: 'Apple'}) }}
</div>
</div>
</div>
</div>
<div class="strike">
<span>{{ $t('or') }}</span>
</div>
<div <div
v-if="registering" v-if="registering"
class="form-group" class="form-group"
@ -496,12 +521,13 @@
} }
.social-icon { .social-icon {
margin-left: 1em;
margin-right: 1em; margin-right: 1em;
width: 18px; width: 18px;
height: 18px; height: 18px;
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;
margin-top: .2em; margin-top: .1em;
} }
} }
@ -581,6 +607,42 @@
.exclamation { .exclamation {
width: 2px; width: 2px;
} }
.strike {
display: block;
text-align: center;
overflow: hidden;
white-space: nowrap;
margin-top: 1.5em;
margin-bottom: 1.5em;
}
.strike > span {
position: relative;
display: inline-block;
line-height: 1.14;
color: #fff;
}
.strike > span:before,
.strike > span:after {
content: "";
position: absolute;
top: 50%;
width: 9999px;
height: 1px;
background: #fff;
}
.strike > span:before {
right: 100%;
margin-right: 15px;
}
.strike > span:after {
left: 100%;
margin-left: 15px;
}
</style> </style>
<script> <script>
@ -589,6 +651,7 @@ import hello from 'hellojs';
import moment from 'moment'; import moment from 'moment';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import isEmail from 'validator/lib/isEmail'; import isEmail from 'validator/lib/isEmail';
import { buildAppleAuthUrl } from '../../libs/auth';
import { MINIMUM_PASSWORD_LENGTH } from '@/../../common/script/constants'; import { MINIMUM_PASSWORD_LENGTH } from '@/../../common/script/constants';
import exclamation from '@/assets/svg/exclamation.svg'; import exclamation from '@/assets/svg/exclamation.svg';
@ -596,6 +659,7 @@ import gryphon from '@/assets/svg/gryphon.svg';
import habiticaIcon from '@/assets/svg/habitica-logo.svg'; import habiticaIcon from '@/assets/svg/habitica-logo.svg';
import facebookSquareIcon from '@/assets/svg/facebook-square.svg'; import facebookSquareIcon from '@/assets/svg/facebook-square.svg';
import googleIcon from '@/assets/svg/google.svg'; import googleIcon from '@/assets/svg/google.svg';
import appleIcon from '@/assets/svg/apple_black.svg';
export default { export default {
data () { data () {
@ -618,6 +682,7 @@ export default {
habiticaIcon, habiticaIcon,
facebookIcon: facebookSquareIcon, facebookIcon: facebookSquareIcon,
googleIcon, googleIcon,
appleIcon,
}); });
return data; return data;
@ -799,35 +864,39 @@ export default {
}, },
// @TODO: Abstract hello in to action or lib // @TODO: Abstract hello in to action or lib
async socialAuth (network) { async socialAuth (network) {
try { if (network === 'apple') {
await hello(network).logout(); window.location.href = buildAppleAuthUrl();
} catch (e) {} // eslint-disable-line
const redirectUrl = `${window.location.protocol}//${window.location.host}`;
const auth = await hello(network).login({
scope: 'email',
// explicitly pass the redirect url or it might redirect to /home
redirect_uri: redirectUrl, // eslint-disable-line camelcase
});
await this.$store.dispatch('auth:socialAuth', {
auth,
});
let redirectTo;
if (this.$route.query.redirectTo) {
redirectTo = this.$route.query.redirectTo;
} else { } else {
redirectTo = '/'; try {
} await hello(network).logout();
} catch (e) {} // eslint-disable-line
// @TODO do not reload entire page const redirectUrl = `${window.location.protocol}//${window.location.host}`;
// problem is that app.vue created hook should be called again const auth = await hello(network).login({
// after user is logged in / just signed up scope: 'email',
// ALSO it's the only way to make sure language data // explicitly pass the redirect url or it might redirect to /home
// is reloaded and correct for the logged in user redirect_uri: redirectUrl, // eslint-disable-line camelcase
window.location.href = redirectTo; });
await this.$store.dispatch('auth:socialAuth', {
auth,
});
let redirectTo;
if (this.$route.query.redirectTo) {
redirectTo = this.$route.query.redirectTo;
} else {
redirectTo = '/';
}
// @TODO do not reload entire page
// problem is that app.vue created hook should be called again
// after user is logged in / just signed up
// ALSO it's the only way to make sure language data
// is reloaded and correct for the logged in user
window.location.href = redirectTo;
}
}, },
handleSubmit () { handleSubmit () {
if (this.registering) { if (this.registering) {

View file

@ -561,6 +561,8 @@ import { SUPPORTED_SOCIAL_NETWORKS } from '@/../../common/script/constants';
import changeClass from '@/../../common/script/ops/changeClass'; import changeClass from '@/../../common/script/ops/changeClass';
import notificationsMixin from '../../mixins/notifications'; import notificationsMixin from '../../mixins/notifications';
import sounds from '../../libs/sounds'; import sounds from '../../libs/sounds';
import { buildAppleAuthUrl } from '../../libs/auth';
// @TODO: this needs our window.env fix // @TODO: this needs our window.env fix
// import { availableLanguages } from '../../../server/libs/i18n'; // import { availableLanguages } from '../../../server/libs/i18n';
@ -837,13 +839,17 @@ export default {
this.text(this.$t('detachedSocial', { network: network.name })); this.text(this.$t('detachedSocial', { network: network.name }));
}, },
async socialAuth (network) { async socialAuth (network) {
const auth = await hello(network).login({ scope: 'email' }); if (network === 'apple') {
window.location.href = buildAppleAuthUrl();
} else {
const auth = await hello(network).login({ scope: 'email' });
await this.$store.dispatch('auth:socialAuth', { await this.$store.dispatch('auth:socialAuth', {
auth, auth,
}); });
window.location.href = '/'; window.location.href = '/';
}
}, },
async changeClassForUser (confirmationNeeded) { async changeClassForUser (confirmationNeeded) {
if (confirmationNeeded && !window.confirm(this.$t('changeClassConfirmCost'))) return; if (confirmationNeeded && !window.confirm(this.$t('changeClassConfirmCost'))) return;

View file

@ -0,0 +1,35 @@
<template>
<div class="static-view">
<p>Redirecting...</p>
</div>
</template>
<style lang='scss'>
@import '~@/assets/scss/static.scss';
.static-view {
height: 400px;
text-align: center;
}
.static-view p {
padding-top: 100px;
font-size: 2em
}
</style>
<script>
export default {
async mounted () {
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const reqParams = { code: urlParams.get('code') };
if (urlParams.has('name')) {
reqParams.name = urlParams.get('name');
}
await this.$store.dispatch('auth:appleAuth', reqParams);
window.location.href = '/';
},
};
</script>

View file

@ -28,31 +28,6 @@
<h3 class="text-center"> <h3 class="text-center">
{{ $t('singUpForFree') }} {{ $t('singUpForFree') }}
</h3> </h3>
<div class="text-center">
<button
class="social-button"
@click="socialAuth('facebook')"
>
<div
class="svg-icon social-icon"
v-html="icons.facebookIcon"
></div>
<span>{{ $t('signUpWithSocial', {social: 'Facebook'}) }}</span>
</button>
<button
class="social-button"
@click="socialAuth('google')"
>
<div
class="svg-icon social-icon"
v-html="icons.googleIcon"
></div>
<span>{{ $t('signUpWithSocial', {social: 'Google'}) }}</span>
</button>
</div>
<div class="strike">
<span>{{ $t('or') }}</span>
</div>
<form <form
class="form" class="form"
@submit.prevent.stop="register()" @submit.prevent.stop="register()"
@ -127,6 +102,41 @@
{{ $t('signup') }} {{ $t('signup') }}
</button> </button>
</form> </form>
<div class="strike">
<span>{{ $t('or') }}</span>
</div>
<div class="text-center">
<button
class="social-button"
@click="socialAuth('facebook')"
>
<div
class="svg-icon social-icon"
v-html="icons.facebookIcon"
></div>
<span>{{ $t('signUpWithSocial', {social: 'Facebook'}) }}</span>
</button>
<button
class="social-button"
@click="socialAuth('google')"
>
<div
class="svg-icon social-icon"
v-html="icons.googleIcon"
></div>
<span>{{ $t('signUpWithSocial', {social: 'Google'}) }}</span>
</button>
<button
class="social-button"
@click="socialAuth('apple')"
>
<div
class="svg-icon social-icon apple-icon"
v-html="icons.appleIcon"
></div>
<span>{{ $t('signUpWithSocial', {social: 'Apple'}) }}</span>
</button>
</div>
</div> </div>
<div class="col-12"> <div class="col-12">
<div <div
@ -450,17 +460,17 @@
} }
h3 { h3 {
font-size: 24px; font-size: 32px;
} }
.social-button { .social-button {
border-radius: 2px; border-radius: 2px;
border: solid 2px #bda8ff; border: solid 2px #bda8ff;
width: 48%; width: 100%;
min-height: 40px; min-height: 40px;
padding: .5em; padding: .5em;
background: transparent; background: transparent;
margin-right: .5em; margin-bottom: .5em;
color: #bda8ff; color: #bda8ff;
transition: .5s; transition: .5s;
@ -481,7 +491,11 @@
height: 18px; height: 18px;
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;
margin-top: .2em; margin-top: .1em;
}
.apple-icon {
margin-top: -1px;
} }
.strike { .strike {
@ -781,6 +795,7 @@
import hello from 'hellojs'; import hello from 'hellojs';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import isEmail from 'validator/lib/isEmail'; import isEmail from 'validator/lib/isEmail';
import { buildAppleAuthUrl } from '../../libs/auth';
import googlePlay from '@/assets/images/home/google-play-badge.svg'; import googlePlay from '@/assets/images/home/google-play-badge.svg';
import iosAppStore from '@/assets/images/home/ios-app-store.svg'; import iosAppStore from '@/assets/images/home/ios-app-store.svg';
import iphones from '@/assets/images/home/iphones.svg'; import iphones from '@/assets/images/home/iphones.svg';
@ -790,6 +805,7 @@ import pixelHorizontal2 from '@/assets/images/home/pixel-horizontal-2.svg';
import pixelHorizontal3 from '@/assets/images/home/pixel-horizontal-3.svg'; import pixelHorizontal3 from '@/assets/images/home/pixel-horizontal-3.svg';
import facebookSquareIcon from '@/assets/svg/facebook-square.svg'; import facebookSquareIcon from '@/assets/svg/facebook-square.svg';
import googleIcon from '@/assets/svg/google.svg'; import googleIcon from '@/assets/svg/google.svg';
import appleIcon from '@/assets/svg/apple.svg';
import cnet from '@/assets/svg/cnet.svg'; import cnet from '@/assets/svg/cnet.svg';
import fastCompany from '@/assets/svg/fast-company.svg'; import fastCompany from '@/assets/svg/fast-company.svg';
import discover from '@/assets/images/home/discover.svg'; import discover from '@/assets/images/home/discover.svg';
@ -814,6 +830,7 @@ export default {
pixelHorizontal3, pixelHorizontal3,
facebookIcon: facebookSquareIcon, facebookIcon: facebookSquareIcon,
googleIcon, googleIcon,
appleIcon,
cnet, cnet,
fastCompany, fastCompany,
discover, discover,
@ -885,9 +902,9 @@ export default {
}); });
hello.init({ hello.init({
facebook: process.env.FACEBOOK_KEY, // eslint-disable-line facebook: process.env.FACEBOOK_KEY, // eslint-disable-line
// windows: WINDOWS_CLIENT_ID, // windows: WINDOWS_CLIENT_ID,
google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line
}); });
}, },
methods: { methods: {
@ -946,22 +963,26 @@ export default {
}, },
// @TODO: Abstract hello in to action or lib // @TODO: Abstract hello in to action or lib
async socialAuth (network) { async socialAuth (network) {
try { if (network === 'apple') {
await hello(network).logout(); window.location.href = buildAppleAuthUrl();
} catch (e) {} // eslint-disable-line } else {
try {
await hello(network).logout();
} catch (e) {} // eslint-disable-line
const redirectUrl = `${window.location.protocol}//${window.location.host}`; const redirectUrl = `${window.location.protocol}//${window.location.host}`;
const auth = await hello(network).login({ const auth = await hello(network).login({
scope: 'email', scope: 'email',
// explicitly pass the redirect url or it might redirect to /home // explicitly pass the redirect url or it might redirect to /home
redirect_uri: redirectUrl, // eslint-disable-line camelcase redirect_uri: redirectUrl, // eslint-disable-line camelcase
}); });
await this.$store.dispatch('auth:socialAuth', { await this.$store.dispatch('auth:socialAuth', {
auth, auth,
}); });
window.location.href = '/'; window.location.href = '/';
}
}, },
}, },
}; };

View file

@ -266,7 +266,7 @@
class="col-12 col-md-3" class="col-12 col-md-3"
> >
<div class="box white row col-12"> <div class="box white row col-12">
<div class="col-9"> <div class="col-9 text-nowrap">
<div :class="stat"> <div :class="stat">
{{ $t(stats[stat].title) }} {{ $t(stats[stat].title) }}
</div> </div>

View file

@ -21,3 +21,8 @@ export function setUpAxios (AUTH_SETTINGS) { // eslint-disable-line import/prefe
return false; return false;
} }
export function buildAppleAuthUrl () {
const redirectUrl = `${window.location.protocol}//${window.location.host}/api/v4/user/auth/apple`;
return `https://appleid.apple.com/auth/authorize?response_mode=form_post&scope=name%20email&response_type=code&version=2&redirect_uri=${redirectUrl}&client_id=${process.env.APPLE_AUTH_CLIENT_ID}`;
}

View file

@ -11,6 +11,7 @@ const StaticWrapper = () => import(/* webpackChunkName: "entry" */'@/components/
const HomePage = () => import(/* webpackChunkName: "entry" */'@/components/static/home'); const HomePage = () => import(/* webpackChunkName: "entry" */'@/components/static/home');
const AppPage = () => import(/* webpackChunkName: "static" */'@/components/static/app'); const AppPage = () => import(/* webpackChunkName: "static" */'@/components/static/app');
const AppleRedirectPage = () => import(/* webpackChunkName: "static" */'@/components/static/appleRedirect');
const ClearBrowserDataPage = () => import(/* webpackChunkName: "static" */'@/components/static/clearBrowserData'); const ClearBrowserDataPage = () => import(/* webpackChunkName: "static" */'@/components/static/clearBrowserData');
const CommunityGuidelinesPage = () => import(/* webpackChunkName: "static" */'@/components/static/communityGuidelines'); const CommunityGuidelinesPage = () => import(/* webpackChunkName: "static" */'@/components/static/communityGuidelines');
const ContactPage = () => import(/* webpackChunkName: "static" */'@/components/static/contact'); const ContactPage = () => import(/* webpackChunkName: "static" */'@/components/static/contact');
@ -272,6 +273,9 @@ const router = new VueRouter({
{ {
name: 'app', path: 'app', component: AppPage, meta: { requiresLogin: false }, name: 'app', path: 'app', component: AppPage, meta: { requiresLogin: false },
}, },
{
name: 'appleRedirect', path: 'apple-redirect', component: AppleRedirectPage, meta: { requiresLogin: false },
},
{ {
name: 'clearBrowserData', path: 'clear-browser-data', component: ClearBrowserDataPage, meta: { requiresLogin: false }, name: 'clearBrowserData', path: 'clear-browser-data', component: ClearBrowserDataPage, meta: { requiresLogin: false },
}, },

View file

@ -82,6 +82,27 @@ export async function socialAuth (store, params) {
localStorage.setItem(LOCALSTORAGE_AUTH_KEY, userLocalData); localStorage.setItem(LOCALSTORAGE_AUTH_KEY, userLocalData);
} }
export async function appleAuth (store, params) {
const url = '/api/v4/user/auth/apple';
const result = await axios.get(url, {
params: {
code: params.code,
name: params.name,
},
});
const user = result.data.data;
const userLocalData = JSON.stringify({
auth: {
apiId: user.id,
apiToken: user.apiToken,
},
});
localStorage.setItem(LOCALSTORAGE_AUTH_KEY, userLocalData);
}
export function logout (store, options = {}) { export function logout (store, options = {}) {
localStorage.clear(); localStorage.clear();
const query = options.redirectToLogin === true ? '?redirectToLogin=true' : ''; const query = options.redirectToLogin === true ? '?redirectToLogin=true' : '';

View file

@ -25,6 +25,7 @@ const envVars = [
'STRIPE_PUB_KEY', 'STRIPE_PUB_KEY',
'FACEBOOK_KEY', 'FACEBOOK_KEY',
'GOOGLE_CLIENT_ID', 'GOOGLE_CLIENT_ID',
'APPLE_AUTH_CLIENT_ID',
'AMPLITUDE_KEY', 'AMPLITUDE_KEY',
'LOGGLY_CLIENT_TOKEN', 'LOGGLY_CLIENT_TOKEN',
// TODO necessary? if yes how not to mess up with vue cli? 'NODE_ENV' // TODO necessary? if yes how not to mess up with vue cli? 'NODE_ENV'

View file

@ -2051,5 +2051,26 @@
"weaponArmoireBaseballBatText": "Baseballschläger", "weaponArmoireBaseballBatText": "Baseballschläger",
"shieldArmoireBaseballGloveNotes": "Perfekt für das große Turnier oder ein freundschaftliches Fangspiel zwischen zwei Aufgaben. Erhöht Stärke um <%= str %>. Verzauberter Schrank: Baseball-Set (Gegenstand 4 von 4).", "shieldArmoireBaseballGloveNotes": "Perfekt für das große Turnier oder ein freundschaftliches Fangspiel zwischen zwei Aufgaben. Erhöht Stärke um <%= str %>. Verzauberter Schrank: Baseball-Set (Gegenstand 4 von 4).",
"armorArmoireBaseballUniformNotes": "Nadelstreifen kommen nie aus der Mode. Erhöht Ausdauer und Stärke um jeweils <%= attrs %>. Verzauberter Schrank: Baseball-Set (Gegenstand 2 von 4).", "armorArmoireBaseballUniformNotes": "Nadelstreifen kommen nie aus der Mode. Erhöht Ausdauer und Stärke um jeweils <%= attrs %>. Verzauberter Schrank: Baseball-Set (Gegenstand 2 von 4).",
"weaponArmoireBaseballBatNotes": "Hol Dir einen Homerun für die guten Gewohnheiten! Erhöht Ausdauer um <%= con %>. Verzauberter Schrank: Baseball-Set (Gegenstand 3 von 4)." "weaponArmoireBaseballBatNotes": "Hol Dir einen Homerun für die guten Gewohnheiten! Erhöht Ausdauer um <%= con %>. Verzauberter Schrank: Baseball-Set (Gegenstand 3 von 4).",
"headAccessoryMystery202004Text": "Mächtige Monarchfalterfühler",
"backMystery202004Text": "Mächtige Monarchfalterflügel",
"shieldSpecialSpring2020HealerText": "Duftschild",
"shieldSpecialSpring2020WarriorText": "Irisierender Schild",
"headSpecialSpring2020HealerText": "Iris-Fascinator",
"headSpecialSpring2020WarriorText": "Käferhelm",
"headSpecialSpring2020RogueText": "Lapislazuli Kabuto-Helm",
"armorSpecialSpring2020HealerText": "Beschützendes Blütenblatt",
"armorSpecialSpring2020MageText": "Whirlpfützenumhang",
"armorSpecialSpring2020WarriorNotes": "Diese harte Schale kann Dich selbst vor den vernichtendsten Attacken schützen. Erhöht Ausdauer um <%= con %>. Limitierte Ausgabe 2020 Frühlingsausrüstung.",
"armorSpecialSpring2020WarriorText": "Exoskelettrüstung",
"armorSpecialSpring2020RogueNotes": "Die Farbe der Dämmerung, einer Vielzahl wertvoller Steine, der tiefsten Stelle des Meeres! Erhöht Wahrnehmung um <%= per %>. Limitierte Ausgabe 2020 Frühlingsausrüstung.",
"armorSpecialSpring2020RogueText": "Ultramarinblaue Rüstung",
"weaponSpecialSpring2020HealerNotes": "Eine Iris ist schön, aber ihre Blätter sind scharf wie Schwerter... lass Dich nicht von den Blumen in die Irre führen, dieser Stab ist hart wie Stahl! Erhöht Intelligenz um <%= int %>. Limitierte Ausgabe 2020 Frühlingsausrüstung.",
"weaponSpecialSpring2020HealerText": "Schwertlilienstab",
"weaponSpecialSpring2020MageNotes": "Sie fallen Dir unaufhörlich auf den Kopf! Aber Du wirst sie nie aufhalten, indem Du Dich beklagst. Erhöht Intelligenz um <%= int %> und Wahrnehmung um <%= per %>. Limitierte Ausgabe 2020 Frühlingsausrüstung.",
"weaponSpecialSpring2020MageText": "Regentropfen",
"weaponSpecialSpring2020WarriorNotes": "Kämpfen oder Fliehen, dieser Flügel wird Dir einen guten Dienst erweisen! Erhöht Stärke um <%= str %>. Limitierte Ausgabe 2020 Frühlingsausrüstung.",
"weaponSpecialSpring2020WarriorText": "Geschliffener Flügel",
"weaponSpecialSpring2020RogueNotes": "Du wirst so schnell zuschlagen, dass sie NOCH blauer aussehen wird! Erhöht Stärke um <%= str %>. Limitierte Ausgabe 2020 Frühlingsausrüstung.",
"weaponSpecialSpring2020RogueText": "Lapislazuli Klinge"
} }

View file

@ -44,7 +44,7 @@
"hatchingPotion": "Schlüpfelixier", "hatchingPotion": "Schlüpfelixier",
"noHatchingPotions": "Du hast im Moment keine Schlüpfelixiere.", "noHatchingPotions": "Du hast im Moment keine Schlüpfelixiere.",
"inventoryText": "Klicke auf ein Ei um die anwendbaren Elixiere grün hervorgehoben zu sehen. Klicke dann auf ein hervorgehobenes Elixier, um Dein Haustier auszubrüten. Falls kein Elixier hervorgehoben wird, klicke auf das Ei um es abzuwählen und klicke diesmal zuerst auf das Elixier, um die Eier hervorzuheben. Du kannst überflüssige Gegenstände auch an Alexander den Händler verkaufen.", "inventoryText": "Klicke auf ein Ei um die anwendbaren Elixiere grün hervorgehoben zu sehen. Klicke dann auf ein hervorgehobenes Elixier, um Dein Haustier auszubrüten. Falls kein Elixier hervorgehoben wird, klicke auf das Ei um es abzuwählen und klicke diesmal zuerst auf das Elixier, um die Eier hervorzuheben. Du kannst überflüssige Gegenstände auch an Alexander den Händler verkaufen.",
"haveHatchablePet": "Du hast ein <%= potion %> Schlüpfelixier und ein <%= egg %>-Ei, um dieses Haustier auszubrüten! <b>Klicke</b> auf den Pfotenabdruck, damit es schlüpft.", "haveHatchablePet": "Du hast ein <%= potion %> Schlüpfelixier und ein <%= egg %>-Ei, um dieses Haustier auszubrüten! <b>Klicke</b>, damit es schlüpft.",
"quickInventory": "Schnell-Inventar", "quickInventory": "Schnell-Inventar",
"foodText": "Futter", "foodText": "Futter",
"food": "Futter und magische Sättel", "food": "Futter und magische Sättel",

View file

@ -542,7 +542,7 @@
"questButterflyText": "Bye, Bye, Butterfry", "questButterflyText": "Bye, Bye, Butterfry",
"questButterflyNotes": "Your gardener friend @Megan sends you an invitation: “These warm days are the perfect time to visit Habiticas butterfly garden in the Taskan countryside. Come see the butterflies migrate!” When you arrive, however, the garden is in shambles -- little more than scorched grass and dried-out weeds. Its been so hot that the Habiticans havent come out to water the flowers, and the dark-red Dailies have turned it into a dry, sun-baked, fire-hazard. There's only one butterfly there, and there's something odd about it...<br><br>“Oh no! This is the perfect hatching ground for the Flaming Butterfry,” cries @Leephon.<br><br>“If we dont catch it, itll destroy everything!” gasps @Eevachu.<br><br>Time to say bye, bye to Butterfry!", "questButterflyNotes": "Your gardener friend @Megan sends you an invitation: “These warm days are the perfect time to visit Habiticas butterfly garden in the Taskan countryside. Come see the butterflies migrate!” When you arrive, however, the garden is in shambles -- little more than scorched grass and dried-out weeds. Its been so hot that the Habiticans havent come out to water the flowers, and the dark-red Dailies have turned it into a dry, sun-baked, fire-hazard. There's only one butterfly there, and there's something odd about it...<br><br>“Oh no! This is the perfect hatching ground for the Flaming Butterfry,” cries @Leephon.<br><br>“If we dont catch it, itll destroy everything!” gasps @Eevachu.<br><br>Time to say bye, bye to Butterfry!",
"questButterflyCompletion": "After a blazing battle, the Flaming Butterfry is captured. “Great job catching the that would-be arsonist,” says @Megan with a sigh of relief. “Still, its hard to vilify even the vilest butterfly. Wed better free this Butterfry someplace safe…like the desert.”<br><br>One of the other gardeners, @Beffymaroo, comes up to you, singed but smiling. “Will you help raise these foundling chrysalises we found? Perhaps next year well have a greener garden for them.”", "questButterflyCompletion": "After a blazing battle, the Flaming Butterfry is captured. “Great job catching that would-be arsonist,” says @Megan with a sigh of relief. “Still, its hard to vilify even the vilest butterfly. Wed better free this Butterfry someplace safe…like the desert.”<br><br>One of the other gardeners, @Beffymaroo, comes up to you, singed but smiling. “Will you help raise these foundling chrysalises we found? Perhaps next year well have a greener garden for them.”",
"questButterflyBoss": "Flaming Butterfry", "questButterflyBoss": "Flaming Butterfry",
"questButterflyDropButterflyEgg": "Caterpillar (Egg)", "questButterflyDropButterflyEgg": "Caterpillar (Egg)",
"questButterflyUnlockText": "Unlocks Caterpillar Eggs for purchase in the Market", "questButterflyUnlockText": "Unlocks Caterpillar Eggs for purchase in the Market",

View file

@ -513,5 +513,12 @@
"backgroundSucculentGardenNotes": "Take in the arid beauty of a Succulent Garden.", "backgroundSucculentGardenNotes": "Take in the arid beauty of a Succulent Garden.",
"backgroundSucculentGardenText": "Succulent Garden", "backgroundSucculentGardenText": "Succulent Garden",
"backgroundButterflyGardenNotes": "Party with pollinators in a Butterfly Garden.", "backgroundButterflyGardenNotes": "Party with pollinators in a Butterfly Garden.",
"backgroundButterflyGardenText": "Butterfly Garden" "backgroundButterflyGardenText": "Butterfly Garden",
"backgroundRainyBarnyardNotes": "Take a soggy splashy stroll through a Rainy Barnyard.",
"backgroundRainyBarnyardText": "Rainy Barnyard",
"backgroundHeatherFieldNotes": "Enjoy the aroma of a Field of Heather.",
"backgroundHeatherFieldText": "Heather Field",
"backgroundAnimalCloudsNotes": "Exercise your imagination finding Animal shapes in the Clouds.",
"backgroundAnimalCloudsText": "Animal Clouds",
"backgrounds042020": "SET 71: Released April 2020"
} }

View file

@ -2083,5 +2083,11 @@
"headAccessoryMystery202004Notes": "They twitch just a bit if the scent of flowers drifts by--use them to find a pretty garden! Confers no benefit. April 2020 Subscriber Item.", "headAccessoryMystery202004Notes": "They twitch just a bit if the scent of flowers drifts by--use them to find a pretty garden! Confers no benefit. April 2020 Subscriber Item.",
"headAccessoryMystery202004Text": "Mighty Monarch Antennae", "headAccessoryMystery202004Text": "Mighty Monarch Antennae",
"backMystery202004Notes": "Make a quick flutter to the nearest flowery meadow or migrate across the continent with these beautiful wings! Confers no benefit. April 2020 Subscriber Item.", "backMystery202004Notes": "Make a quick flutter to the nearest flowery meadow or migrate across the continent with these beautiful wings! Confers no benefit. April 2020 Subscriber Item.",
"backMystery202004Text": "Mighty Monarch Wings" "backMystery202004Text": "Mighty Monarch Wings",
"shieldArmoireHobbyHorseNotes": "Ride your handsome hobby-horse steed toward your just Rewards! Increases Perception and Constitution by <%= attrs %> each. Enchanted Armoire: Paper Knight Set (Item 2 of 3).",
"shieldArmoireHobbyHorseText": "Hobby Horse",
"armorArmoireBoxArmorNotes": "Box Armor: It fits, therefore you sits... uh, therefore you wear it into battle, like the bold knight you are! Increases Perception and Constitution by <%= attrs %> each. Enchanted Armoire: Paper Knight Set (Item 3 of 3). ",
"armorArmoireBoxArmorText": "Box Armor",
"weaponArmoirePaperCutterNotes": "This may not look fearsome, but have you never had a papercut? Increases Strength by <%= str %>. Enchanted Armoire: Paper Knight Set (Item 1 of 3).",
"weaponArmoirePaperCutterText": "Paper Cutter"
} }

View file

@ -44,7 +44,7 @@
"hatchingPotion": "hatching potion", "hatchingPotion": "hatching potion",
"noHatchingPotions": "You don't have any hatching potions.", "noHatchingPotions": "You don't have any hatching potions.",
"inventoryText": "Click an egg to see usable potions highlighted in green and then click one of the highlighted potions to hatch your pet. If no potions are highlighted, click that egg again to deselect it, and instead click a potion first to have the usable eggs highlighted. You can also sell unwanted drops to Alexander the Merchant.", "inventoryText": "Click an egg to see usable potions highlighted in green and then click one of the highlighted potions to hatch your pet. If no potions are highlighted, click that egg again to deselect it, and instead click a potion first to have the usable eggs highlighted. You can also sell unwanted drops to Alexander the Merchant.",
"haveHatchablePet": "You have a <%= potion %> hatching potion and <%= egg %> egg to hatch this pet! <b>Click</b> the paw print to hatch.", "haveHatchablePet": "You have a <%= potion %> hatching potion and <%= egg %> egg to hatch this pet! <b>Click</b> to hatch!",
"quickInventory": "Quick Inventory", "quickInventory": "Quick Inventory",
"foodText": "food", "foodText": "food",
"food": "Pet Food and Saddles", "food": "Pet Food and Saddles",

View file

@ -513,5 +513,12 @@
"backgroundButterflyGardenText": "Jardin à papillons", "backgroundButterflyGardenText": "Jardin à papillons",
"backgroundAmongGiantFlowersNotes": "Badinez parmi les fleurs géantes.", "backgroundAmongGiantFlowersNotes": "Badinez parmi les fleurs géantes.",
"backgroundAmongGiantFlowersText": "Parmi les fleurs géantes", "backgroundAmongGiantFlowersText": "Parmi les fleurs géantes",
"backgrounds032020": "Ensemble 70 : sorti en mars 2020" "backgrounds032020": "Ensemble 70 : sorti en mars 2020",
"backgroundRainyBarnyardNotes": "Faites une promenade détrempée et éclaboussante dans une basse-cour pluvieuse.",
"backgroundRainyBarnyardText": "Basse-cour pluvieuse",
"backgroundHeatherFieldNotes": "Appréciez les arômes d'un champ de bruyère.",
"backgroundHeatherFieldText": "Champ de bruyère",
"backgroundAnimalCloudsNotes": "Entraînez votre imagination à reconnaître les formes des animaux dans les nuages.",
"backgroundAnimalCloudsText": "Nuages en formes d'animaux",
"backgrounds042020": "Ensemble 71 : sorti en avril 2020"
} }

View file

@ -2079,5 +2079,15 @@
"weaponSpecialSpring2020WarriorNotes": "En vol ou au sol, ces ailes vous serviront bien ! Augmente la force de <%= str %>. Équipement en édition limitée du printemps 2020.", "weaponSpecialSpring2020WarriorNotes": "En vol ou au sol, ces ailes vous serviront bien ! Augmente la force de <%= str %>. Équipement en édition limitée du printemps 2020.",
"weaponSpecialSpring2020WarriorText": "Ailes affutées", "weaponSpecialSpring2020WarriorText": "Ailes affutées",
"weaponSpecialSpring2020RogueNotes": "Vous frapperez si vite que aura l'air encore PLUS bleu ! Augmente la force de <%= str %>. Équipement en édition limitée du printemps 2020.", "weaponSpecialSpring2020RogueNotes": "Vous frapperez si vite que aura l'air encore PLUS bleu ! Augmente la force de <%= str %>. Équipement en édition limitée du printemps 2020.",
"weaponSpecialSpring2020RogueText": "Épée de Lazurite" "weaponSpecialSpring2020RogueText": "Épée de Lazurite",
"headAccessoryMystery202004Notes": "Elles se trémoussent juste un peu si l'odeur des fleurs passe devant elles : utilisez-les pour trouver un joli jardin ! Ne confère aucun bonus. Équipement d'abonnement d'avril 2020.",
"headAccessoryMystery202004Text": "Antennes de merveilleux monarque",
"backMystery202004Notes": "Faites un vol rapide vers la prairie fleurie la plus proche ou migrez à travers le continent avec ces jolies ailes ! Ne confère aucun bonus. Équipement d'abonnement d'avril 2020.",
"backMystery202004Text": "Ailes de merveilleux monarque",
"shieldArmoireHobbyHorseNotes": "Chevauchez votre magnifique monture de jeu vers vos récompenses méritées ! Augmente la perception et la constitution de <%= attrs %> chacune. Armoire enchantée : ensemble de chevalerie de papier (objet 2 de 3).",
"shieldArmoireHobbyHorseText": "Cheval de jeu",
"armorArmoireBoxArmorNotes": "Attention, un chat y dort peut-être... mais ça ne vous empêchera pas de porter cette armure au combat, avec l'audace qui vous caractérise ! Augmente la perception et la constitution de <%= attrs %> chacune. Armoire enchantée : ensemble de chevalerie de papier (objet 3 de 3). ",
"armorArmoireBoxArmorText": "Armure boite",
"weaponArmoirePaperCutterNotes": "Ca n'a pas l'air terrible, mais vous êtes vous déjà coupé avec une feuille de papier ? Augmente la force de <%= str %>. Armoire enchantée : ensemble de chevalerie de papier (objet 1 de 3).",
"weaponArmoirePaperCutterText": "Coupe papier"
} }

View file

@ -44,7 +44,7 @@
"hatchingPotion": "potion d'éclosion", "hatchingPotion": "potion d'éclosion",
"noHatchingPotions": "Vous n'avez pas de potion d'éclosion.", "noHatchingPotions": "Vous n'avez pas de potion d'éclosion.",
"inventoryText": "Cliquez sur un œuf pour voir les potions utilisables surlignées en vert, puis cliquez sur une des potions surlignées pour faire éclore votre familier. Si aucune potion n'est surlignée, cliquez à nouveau sur lœuf pour le désélectionner et cliquez plutôt sur une potion d'abord pour voir les œufs utilisables. Vous pouvez aussi vendre votre surplus d'objets à Alexander le marchand.", "inventoryText": "Cliquez sur un œuf pour voir les potions utilisables surlignées en vert, puis cliquez sur une des potions surlignées pour faire éclore votre familier. Si aucune potion n'est surlignée, cliquez à nouveau sur lœuf pour le désélectionner et cliquez plutôt sur une potion d'abord pour voir les œufs utilisables. Vous pouvez aussi vendre votre surplus d'objets à Alexander le marchand.",
"haveHatchablePet": "Vous avez une potion d'éclosion <%= potion %> et un œuf de <%= egg %> qui peuvent faire éclore ce familier ! <b>Cliquez</b> sur l'empreinte pour le faire naître.", "haveHatchablePet": "Vous avez une potion d'éclosion <%= potion %> et un œuf de <%= egg %> qui peuvent faire éclore ce familier ! <b>Cliquez</b> pour le faire naître !",
"quickInventory": "Inventaire rapide", "quickInventory": "Inventaire rapide",
"foodText": "nourriture", "foodText": "nourriture",
"food": "Nourriture de familiers et selles", "food": "Nourriture de familiers et selles",

View file

@ -247,5 +247,6 @@
"monthlyMysteryItems": "Objets mystère mensuels", "monthlyMysteryItems": "Objets mystère mensuels",
"subscribersReceiveBenefits": "Les personnes abonnées reçoivent ces bénéfices utiles !", "subscribersReceiveBenefits": "Les personnes abonnées reçoivent ces bénéfices utiles !",
"mysterySet202003": "Ensemble de combat barbelé", "mysterySet202003": "Ensemble de combat barbelé",
"giftASubscription": "Offrez un abonnement" "giftASubscription": "Offrez un abonnement",
"mysterySet202004": "Ensemble du merveilleux monarque"
} }

View file

@ -513,5 +513,12 @@
"backgroundHolidayMarketNotes": "Vind de perfecte cadeaus en versieringen bij een Feestdagen Markt.", "backgroundHolidayMarketNotes": "Vind de perfecte cadeaus en versieringen bij een Feestdagen Markt.",
"backgroundHolidayMarketText": "Feestdagen Markt", "backgroundHolidayMarketText": "Feestdagen Markt",
"backgrounds122019": "SET 67: Uitgebracht December 2019", "backgrounds122019": "SET 67: Uitgebracht December 2019",
"backgroundPotionShopNotes": "Vind een elixer tegen een kwaal in een Uitbroeddrankenwinkel." "backgroundPotionShopNotes": "Vind een elixer tegen een kwaal in een Uitbroeddrankenwinkel.",
"backgroundRainyBarnyardNotes": "Maak een drassige, spetterende wandeling door een Regenachtige Boerenerf.",
"backgroundRainyBarnyardText": "Regenachtige boerenerf",
"backgroundHeatherFieldNotes": "Geniet van de geur van een Heather Veld.",
"backgroundHeatherFieldText": "Heather Veld",
"backgroundAnimalCloudsNotes": "Oefen je fantasie door dierenvormen in de wolken te vinden.",
"backgroundAnimalCloudsText": "Dierlijke Wolken",
"backgrounds042020": "SET 71: Uitgekomen April 2020"
} }

View file

@ -1,6 +1,6 @@
{ {
"jsDisabledHeading": "Helaas! Je browser heeft Javascript niet geactiveerd", "jsDisabledHeading": "Helaas! Je browser heeft JavaScript niet geactiveerd",
"jsDisabledHeadingFull": "Helaas! Je browser heeft Javascript niet geactiveerd en zonder dat kan Habitica niet goed werken", "jsDisabledHeadingFull": "Helaas! Je browser heeft JavaScript niet geactiveerd en zonder dat kan Habitica niet goed werken",
"jsDisabledText": "Habitica kan zonder dat de site niet goed weergeven!", "jsDisabledText": "Habitica kan zonder dat de site niet goed weergeven!",
"jsDisabledLink": "Activeer alsjeblieft Javascript om door te gaan!" "jsDisabledLink": "Activeer alsjeblieft JavaScript om door te gaan!"
} }

View file

@ -16,7 +16,7 @@
"randomize": "Randomiseren", "randomize": "Randomiseren",
"mattBoch": "Matt Boch", "mattBoch": "Matt Boch",
"mattShall": "Zal ik je je ros brengen, <%= name %>? Als je een Huisdier genoeg voedsel hebt gevoerd om het in een Rijdier te veranderen, zal het hier verschijnen. Klik op een rijdier om het te zadelen!", "mattShall": "Zal ik je je ros brengen, <%= name %>? Als je een Huisdier genoeg voedsel hebt gevoerd om het in een Rijdier te veranderen, zal het hier verschijnen. Klik op een rijdier om het te zadelen!",
"mattBochText1": "Welkom in de Stal! Ik ben Matt, de Dierenmeester. Na niveau 3 kun je huisdieren laten uitkomen door middel van eieren en toverdranken. Als je een dier laat uitkomen in de markt, zal het hier verschijnen! Klik op de afbeelding van een huisdier om het aan je avatar toe te voegen! Voeder je huisdieren met het voedsel dat je vindt na niveau 3, zodat ze uitgroeien tot krachtige rijdieren.", "mattBochText1": "Welkom in de Stal! Ik ben Matt, de dierenmeester. Iedere keer als je een taak voltooid, heb je een willekeurige kans om een Ei of een Uitbroedtoverdrankje te ontvangen om Huisdieren uit te broeden. Als je een Huisdier uitbroedt, verschijnt het hier! Klik op de afbeelding van een Huisdier om het toe te voegen aan je Avatar. Voer ze met Voedsel voor Huisdieren en ze zullen uitgroeien tot stevige Rijdieren.",
"welcomeToTavern": "Welkom bij de Herberg!", "welcomeToTavern": "Welkom bij de Herberg!",
"sleepDescription": "Heb je een pauze nodig? Neem een kijkje in Daniel's Herberg om enkele van de moeilijkere spelmechanismen te pauzeren:", "sleepDescription": "Heb je een pauze nodig? Neem een kijkje in Daniel's Herberg om enkele van de moeilijkere spelmechanismen te pauzeren:",
"sleepBullet1": "Gemiste Dagtaken zullen je niet beschadigen", "sleepBullet1": "Gemiste Dagtaken zullen je niet beschadigen",
@ -91,7 +91,7 @@
"unlocked": "Voorwerpen zijn ontgrendeld", "unlocked": "Voorwerpen zijn ontgrendeld",
"alreadyUnlocked": "Volledige set is al ontgrendeld.", "alreadyUnlocked": "Volledige set is al ontgrendeld.",
"alreadyUnlockedPart": "Volledige set is al gedeeltelijk ontgrendeld.", "alreadyUnlockedPart": "Volledige set is al gedeeltelijk ontgrendeld.",
"invalidQuantity": "Hoeveelheid om te kopen moet een nummer zijn.", "invalidQuantity": "Hoeveelheid om te kopen moet een positief heel getal zijn.",
"USD": "(USD)", "USD": "(USD)",
"newStuff": "Nieuwe informatie van Bailey", "newStuff": "Nieuwe informatie van Bailey",
"newBaileyUpdate": "Nieuwe update van Bailey!", "newBaileyUpdate": "Nieuwe update van Bailey!",
@ -103,7 +103,7 @@
"donationDesc": "20 Edelstenen, donatie aan Habitica", "donationDesc": "20 Edelstenen, donatie aan Habitica",
"payWithCard": "Betaal met creditcard", "payWithCard": "Betaal met creditcard",
"payNote": "Opmerking: PayPal doet er soms lang over om een betaling te verwerken. We raden je aan te betalen met een creditcard.", "payNote": "Opmerking: PayPal doet er soms lang over om een betaling te verwerken. We raden je aan te betalen met een creditcard.",
"card": "CreditCard (met Stripe)", "card": "Creditcard",
"amazonInstructions": "Klik op de knop om te betalen via de Amazon Betaaldienst", "amazonInstructions": "Klik op de knop om te betalen via de Amazon Betaaldienst",
"paymentMethods": "Koop met", "paymentMethods": "Koop met",
"paymentSuccessful": "Je betaling van succesvol!", "paymentSuccessful": "Je betaling van succesvol!",
@ -138,9 +138,9 @@
"tourPartyPage": "Jouw gezelschap helpt je verantwoordelijk te blijven. Nodig je vrienden uit en speel een queeste-perkamentrol vrij!", "tourPartyPage": "Jouw gezelschap helpt je verantwoordelijk te blijven. Nodig je vrienden uit en speel een queeste-perkamentrol vrij!",
"tourGuildsPage": "Gilden zijn chatgroepen met gezamenlijke interesses; gemaakt door de spelers, voor de spelers. Zoek door de lijst en word lid van de gilden die je interesseren. Kijk eens bij het populaire Habitica Help: Ask a Question gilde, waar iedereen vragen kan stellen over Habitica!", "tourGuildsPage": "Gilden zijn chatgroepen met gezamenlijke interesses; gemaakt door de spelers, voor de spelers. Zoek door de lijst en word lid van de gilden die je interesseren. Kijk eens bij het populaire Habitica Help: Ask a Question gilde, waar iedereen vragen kan stellen over Habitica!",
"tourChallengesPage": "Uitdagingen zijn takenlijsten met een thema, aangemaakt door andere gebruikers! Als je meedoet aan een uitdaging worden de bijbehorende taken toegevoegd aan je account. Wedijver met andere gebruikers om edelstenen te winnen!", "tourChallengesPage": "Uitdagingen zijn takenlijsten met een thema, aangemaakt door andere gebruikers! Als je meedoet aan een uitdaging worden de bijbehorende taken toegevoegd aan je account. Wedijver met andere gebruikers om edelstenen te winnen!",
"tourMarketPage": "Vanaf niveau 3 kun je eieren en uitbroeddranken vinden als je een taak afvinkt. Ze verschijnen hier - gebruik ze om huisdieren uit te broeden! Je kunt ook voorwerpen kopen in de markt.", "tourMarketPage": "Iedere keer als je een taak voltooid, heb je een willekeurige kans om een Ei, Uitbroedtoverdrank of een stuk Voedsel voor Dieren te vinden. Je kunt deze voorwerpen ook hier kopen.",
"tourHallPage": "Welkom in de Hal der Helden, waar mensen die een bijdrage leveren aan Habitica worden geëerd. Door programmeren, kunst, muziek, schrijven, of zelfs door behulpzaam te zijn hebben zij edelstenen, exclusieve uitrusting en prestigieuze titels verdiend. Als je wilt, kun jij ook bijdragen aan Habitica!", "tourHallPage": "Welkom in de Hal der Helden, waar mensen die een bijdrage leveren aan Habitica worden geëerd. Door programmeren, kunst, muziek, schrijven, of zelfs door behulpzaam te zijn hebben zij edelstenen, exclusieve uitrusting en prestigieuze titels verdiend. Als je wilt, kun jij ook bijdragen aan Habitica!",
"tourPetsPage": "Dit is de Stal! Na niveau 3 verzamel je eieren en toverdranken tijdens het voltooien van taken. Als je een huisdier laat uitkomen in de markt, verschijnt het hier! Klik op het plaatje van een huisdier om het aan je avatar toe te voegen. Voer je huisdieren met het voedsel dat je na niveau 3 vindt om ze te laten uitgroeien tot krachtige rijdieren.", "tourPetsPage": "Welkom in de Stal! Iedere keer als je een taak voltooid, heb je een willekeurige kans om een Ei of een Uitbroedtoverdrankje te ontvangen om Huisdieren uit te broeden. Als je een Huisdier uitbroedt, verschijnt het hier! Klik op de afbeelding van een Huisdier om het toe te voegen aan je Avatar. Voer ze met Voedsel voor Huisdieren en ze zullen uitgroeien tot stevige Rijdieren.",
"tourMountsPage": "Als je een huisdier genoeg gevoerd hebt en het in een rijdier verandert, verschijnt het hier. Klik op een rijdier om op te zadelen!", "tourMountsPage": "Als je een huisdier genoeg gevoerd hebt en het in een rijdier verandert, verschijnt het hier. Klik op een rijdier om op te zadelen!",
"tourEquipmentPage": "This is where your Equipment is stored! Your Battle Gear affects your Stats. If you want to show different Equipment on your avatar without changing your Stats, click \"Enable Costume.\"", "tourEquipmentPage": "This is where your Equipment is stored! Your Battle Gear affects your Stats. If you want to show different Equipment on your avatar without changing your Stats, click \"Enable Costume.\"",
"equipmentAlreadyOwned": "Je bezit dat stuk uitrusting al", "equipmentAlreadyOwned": "Je bezit dat stuk uitrusting al",
@ -167,5 +167,9 @@
"welcome4": "Vermijd slechte gewoontes die je gezondheid (HP) verminderen, want anders gaat je avatar dood!", "welcome4": "Vermijd slechte gewoontes die je gezondheid (HP) verminderen, want anders gaat je avatar dood!",
"welcome5": "Nu kun je je avatar aanpassen en je taken instellen...", "welcome5": "Nu kun je je avatar aanpassen en je taken instellen...",
"imReady": "Betreed Habitica", "imReady": "Betreed Habitica",
"limitedOffer": "Beschikbaar tot <%= date %>" "limitedOffer": "Beschikbaar tot <%= date %>",
"paymentSubBillingWithMethod": "Je abonnement zal afgeschreven worden: <strong>$<%= amount %></strong> elke <strong><%= months %> maanden</strong> via <strong><%= paymentMethod %></strong>.",
"paymentCanceledDisputes": "We hebben een annuleringsbevestiging gestuurd naar je e-mailadres. Als je deze mail niet ziet, neem dan alsjeblieft contact met ons op om geschillen over toekomstige rekeningen te voorkomen.",
"paymentAutoRenew": "Dit abonnement zal automatisch vernieuwd worden totdat het wordt geannuleerd. Als het nodig is je abonnement op te zeggen, kun je dat doen in je instellingen.",
"cannotUnpinItem": "Dit voorwerp kan niet losgekoppeld worden."
} }

View file

@ -5,6 +5,6 @@
"step2": "Stap 2: Verdien punten door dingen te doen in het echte leven", "step2": "Stap 2: Verdien punten door dingen te doen in het echte leven",
"webStep2Text": "Begin nu met het tackelen van de doelen op je lijst! Als je taken voltooit en ze afstreept in Habitica krijg je [Ervaringspunten](https://habitica.fandom.com/nl/wiki/Ervaringspunten), die je helpen om niveaus omhoog te gaan, en [Goud](https://habitica.fandom.com/nl/wiki/Goud), waarmee je beloningen kunt kopen. Als je terugvalt in slechte gewoontes of dagelijkse taken mist, zul je [Levenspunten](https://habitica.fandom.com/nl/wiki/Levenspunten) verliezen. Op deze manier zijn de ervaringsbalk en gezondheidsbalk een leuke indicator van je vooruitgang ten opzichte van je doelen. Je zult zien dat je echte leven verbetert naarmate je personage in het spel vooruit gaat.", "webStep2Text": "Begin nu met het tackelen van de doelen op je lijst! Als je taken voltooit en ze afstreept in Habitica krijg je [Ervaringspunten](https://habitica.fandom.com/nl/wiki/Ervaringspunten), die je helpen om niveaus omhoog te gaan, en [Goud](https://habitica.fandom.com/nl/wiki/Goud), waarmee je beloningen kunt kopen. Als je terugvalt in slechte gewoontes of dagelijkse taken mist, zul je [Levenspunten](https://habitica.fandom.com/nl/wiki/Levenspunten) verliezen. Op deze manier zijn de ervaringsbalk en gezondheidsbalk een leuke indicator van je vooruitgang ten opzichte van je doelen. Je zult zien dat je echte leven verbetert naarmate je personage in het spel vooruit gaat.",
"step3": "Stap 3: Bewerk en verken Habitica", "step3": "Stap 3: Bewerk en verken Habitica",
"webStep3Text": "Zodra je wat meer vertrouwd bent met de basis, kun je nog meer uit Habitica halen met deze vernuftige functies: \n * Organiseer je taken met [labels](http://habitica.fandom.com/wiki/Tags) (wijzig de taak om labels toe te voegen).\n * Pas je [avatar] aan (http://habitica.fandom.com/wiki/Avatar) door te klikken op het gebruikersicoon in de hoek rechtsboven. \n * Koop je [Uitrusting](http://habitica.fandom.com/wiki/Equipment) in de Beloningensectie of in de [Winkels](<%= shopUrl %>), en verander het via [Boedel> Uitrusting](<%= equipUrl %>).\n * Bouw een band op met andere gebruikers in de [Herberg](http://habitica.fandom.com/wiki/Tavern).\n * Vanaf niveau 3, broedt [Huisdieren] uit (http://habitica.fandom.com/wiki/Pets) door [Eieren] (http://habitica.fandom.com/wiki/Eggs) en [Uitbroeddrankjes](http://habitica.fandom.com/wiki/Hatching_Potions) te verzamelen. [Voed](http://habitica.fandom.com/wiki/Food) ze om ze te laten uitgroeien tot [Rijdieren](http://habitica.fandom.com/wiki/Mounts).\n * Op niveau 10: Kies een specifieke [Klasse](http://habitica.fandom.com/wiki/Class_System) en gebruik de klassespecifieke [vaardigheden](http://habitica.fandom.com/wiki/Skills) (levels 11 to 14).\n * Vorm een gezelschap met je vrienden (door te klikken op [Gezelschap](<%= partyUrl %>) in de navigatiebalk) en zo verantwoording af te leggen en om Queesterollen te verdienen.\n * Versla Monsters en verzamel voorwerpen op [queesten](http://habitica.fandom.com/wiki/Quests) (Je ontvangt een Queeste op niveau 15).", "webStep3Text": "Zodra je wat meer vertrouwd bent met de basis, kun je nog meer uit Habitica halen met deze vernuftige functies: \n * Organiseer je taken met [labels](http://habitica.fandom.com/wiki/Tags) (wijzig de taak om labels toe te voegen).\n * Pas je [avatar] aan (http://habitica.fandom.com/wiki/Avatar) door te klikken op het gebruikersicoon in de hoek rechtsboven. \n * Koop je [Uitrusting](http://habitica.fandom.com/wiki/Equipment) in de Beloningensectie of in de [Winkels](<%= shopUrl %>), en verander het via [Boedel> Uitrusting](<%= equipUrl %>).\n * Bouw een band op met andere gebruikers in de [Herberg](http://habitica.fandom.com/wiki/Tavern).\n * Broedt [Huisdieren] uit (http://habitica.fandom.com/wiki/Pets) door [Eieren] (http://habitica.fandom.com/wiki/Eggs) en [Uitbroeddrankjes](http://habitica.fandom.com/wiki/Hatching_Potions) te verzamelen. [Voed](http://habitica.fandom.com/wiki/Food) ze om ze te laten uitgroeien tot [Rijdieren](http://habitica.fandom.com/wiki/Mounts).\n * Op niveau 10: Kies een specifieke [Klasse](http://habitica.fandom.com/wiki/Class_System) en gebruik de klassespecifieke [vaardigheden](http://habitica.fandom.com/wiki/Skills) (levels 11 to 14).\n * Vorm een gezelschap met je vrienden (door te klikken op [Gezelschap](<%= partyUrl %>) in de navigatiebalk) en zo verantwoording af te leggen en om Queesterollen te verdienen.\n * Versla Monsters en verzamel voorwerpen op [queesten](http://habitica.fandom.com/wiki/Quests) (Je ontvangt een Queeste op niveau 15).",
"overviewQuestions": "Heb je vragen? Bekijk de [FAQ](<%= faqUrl %>)! Als dat je vraag niet beantwoordt, dan kun je hulp zoeken bij het [Habitica Help guild](<%= helpGuildUrl %>).\n\n Veel succes met je taken!" "overviewQuestions": "Heb je vragen? Bekijk de [FAQ](<%= faqUrl %>)! Als dat je vraag niet beantwoordt, dan kun je hulp zoeken bij het [Habitica Help guild](<%= helpGuildUrl %>).\n\n Veel succes met je taken!"
} }

View file

@ -44,7 +44,7 @@
"hatchingPotion": "uitbroeddrank", "hatchingPotion": "uitbroeddrank",
"noHatchingPotions": "Je hebt geen uitbroeddranken.", "noHatchingPotions": "Je hebt geen uitbroeddranken.",
"inventoryText": "Klik op een ei om bruikbare drankjes in het groen gemarkeerd te zien en klik dan op één van de gemarkeerde drankjes om een dier uit te broeden. Als er geen drankjes zijn gemarkeerd, klik dan opnieuw op het ei om de selectie te verwijderen; klik in plaats daarvan op een drankje om te zien of er bruikbare eieren gemarkeerd worden. Je kunt ongewenste voorwerpen ook verkopen aan Alexander de Koopman.", "inventoryText": "Klik op een ei om bruikbare drankjes in het groen gemarkeerd te zien en klik dan op één van de gemarkeerde drankjes om een dier uit te broeden. Als er geen drankjes zijn gemarkeerd, klik dan opnieuw op het ei om de selectie te verwijderen; klik in plaats daarvan op een drankje om te zien of er bruikbare eieren gemarkeerd worden. Je kunt ongewenste voorwerpen ook verkopen aan Alexander de Koopman.",
"haveHatchablePet": "Je hebt een <%= potion %>uitbroeddrank en een <%= egg %>ei om dit huisdier te laten uitbroeden! <b>Click</b> op de pootafdruk om uit te broeden.", "haveHatchablePet": "Je hebt een <%= potion %>uitbroeddrank en een <%= egg %>ei om dit huisdier te laten uitbroeden! <b>Click</b> om uit te broeden.",
"quickInventory": "Snelle boedel", "quickInventory": "Snelle boedel",
"foodText": "voedsel", "foodText": "voedsel",
"food": "Huisdieren voedsel en zadels", "food": "Huisdieren voedsel en zadels",
@ -123,9 +123,9 @@
"foodWikiText": "Wat eet mijn huisdier graag?", "foodWikiText": "Wat eet mijn huisdier graag?",
"foodWikiUrl": "http://habitica.fandom.com/wiki/Food_Preferences", "foodWikiUrl": "http://habitica.fandom.com/wiki/Food_Preferences",
"welcomeStable": "Welkom in de stal!", "welcomeStable": "Welkom in de stal!",
"welcomeStableText": "Ik ben Matt, de Dierenmeester. Na niveau 3 kun je huisdieren laten uitkomen door middel van eieren en toverdranken die je vind! Wanneer je een dier laat uitkomen vanaf je Boedel, zal deze hier verschijnen! Klik op de afbeelding van een huisdier om het aan je avatar toe te voegen. Geef je huisdieren te eten met het voedsel dat je vindt na niveau 3, zodat ze uitgroeien tot krachtige rijdieren.", "welcomeStableText": "Welkom bij de stal! Ik ben Matt, de beestmeester. Elke keer dat je een taak voltooit, heb je een willekeurige kans om een ei of een uitkomstdrankje te ontvangen om huisdieren uit te laten komen. Wanneer je een huisdier uitbroedt, zal het hier verschijnen! Klik op de afbeelding van een huisdier om deze toe te voegen aan je avatar. Voer ze met het dierenvoedsel dat je vindt en ze zullen uitgroeien tot winterharde rijdieren.",
"petLikeToEat": "Wat eet mijn huisdier graag?", "petLikeToEat": "Wat eet mijn huisdier graag?",
"petLikeToEatText": "Het maakt niet uit wat je huisdieren voert om ze te laten groeien, maar ze zullen sneller groeien als je ze hun favoriete Voedsel voert. Experimenteer om uit te vinden wat het patroon is, of zie het antwoord hier: <br/><a href=\"http://habitica.fandom.com/wiki/Food_Preferences\" target=\"_blank\">http://habitica.fandom.com/wiki/Food_Preferences</a>", "petLikeToEatText": "Het maakt niet uit wat je huisdieren voert om ze te laten groeien, maar ze zullen sneller groeien als je ze hun favoriete voedsel voert. Experimenteer om uit te vinden wat het patroon is, of zie het antwoord hier: <br/><a href=\"http://habitica.fandom.com/wiki/Food_Preferences\" target=\"_blank\">http://habitica.fandom.com/wiki/Food_Preferences</a>",
"filterByStandard": "Standaard", "filterByStandard": "Standaard",
"filterByMagicPotion": "Magisch drankje", "filterByMagicPotion": "Magisch drankje",
"filterByQuest": "Queeste", "filterByQuest": "Queeste",

View file

@ -21,7 +21,7 @@
"rebirthOrb": "Heeft een Bol der Hergeboorte gebruikt om opnieuw te beginnen na het bereiken van Niveau <%= level %>.", "rebirthOrb": "Heeft een Bol der Hergeboorte gebruikt om opnieuw te beginnen na het bereiken van Niveau <%= level %>.",
"rebirthOrb100": "Heeft een Bol der Hergeboorte gebruikt om opnieuw te beginnen na het bereiken van Niveau 100 of hoger.", "rebirthOrb100": "Heeft een Bol der Hergeboorte gebruikt om opnieuw te beginnen na het bereiken van Niveau 100 of hoger.",
"rebirthOrbNoLevel": "Heeft een Bol der Hergeboorte gebruikt om opnieuw te beginnen.", "rebirthOrbNoLevel": "Heeft een Bol der Hergeboorte gebruikt om opnieuw te beginnen.",
"rebirthPop": "Herstart je personage direct als een Niveau 1 Krijger zonder je prestaties, verzamelobjecten en uitrusting te verliezen. Je taken en hun geschiedenis zal hetzelfde blijven maar ze worden gereset naar geel. Je series worden verwijderd behalve bij taken van uitdagingen. Je Goud, Ervaring, Mana en de effecten van al je Vaardigheden gaan verloren. Dit alles gaat direct in werking. Voor meer informatie zie de pagina van de <a href='http://habitica.fandom.com/wiki/Orb_of_Rebirth' target='_blank'>Bol der Hergeboorte</a>.", "rebirthPop": "Herstart je personage direct als een Niveau 1 Krijger zonder je prestaties, verzamelobjecten en uitrusting te verliezen. Je taken - behalve taken die horen uitdagingen of onderdeel zijn van een groepsplan - en hun geschiedenis zal hetzelfde blijven maar ze worden gereset naar een gele kleur en de series worden verwijderd. Je Goud, Ervaring, Mana en de effecten van al je Vaardigheden gaan verloren. Dit alles gaat direct in werking. Voor meer informatie zie de pagina van de <a href='http://habitica.fandom.com/wiki/Orb_of_Rebirth' target='_blank'>Bol der Hergeboorte</a>.",
"rebirthName": "Bol der Hergeboorte", "rebirthName": "Bol der Hergeboorte",
"reborn": "Herboren, maximale Niveau <%= reLevel %>", "reborn": "Herboren, maximale Niveau <%= reLevel %>",
"confirmReborn": "Weet je het zeker?", "confirmReborn": "Weet je het zeker?",

View file

@ -158,7 +158,7 @@
"mysterySet301703": "Pauw steampunkset", "mysterySet301703": "Pauw steampunkset",
"mysterySet301704": "Fazant steampunkset", "mysterySet301704": "Fazant steampunkset",
"mysterySetwondercon": "Wondercon", "mysterySetwondercon": "Wondercon",
"subUpdateCard": "Creditcard aanpassen", "subUpdateCard": "Creditcard gegevens aanpassen",
"subUpdateTitle": "Aanpassen", "subUpdateTitle": "Aanpassen",
"subUpdateDescription": "Verander welke creditcard gebruikt wordt.", "subUpdateDescription": "Verander welke creditcard gebruikt wordt.",
"notEnoughHourglasses": "Je hebt niet genoeg mystieke zandlopers.", "notEnoughHourglasses": "Je hebt niet genoeg mystieke zandlopers.",
@ -247,5 +247,6 @@
"monthlyMysteryItems": "Maandelijkse mysterie-voorwerpen", "monthlyMysteryItems": "Maandelijkse mysterie-voorwerpen",
"subscribersReceiveBenefits": "Abonnees ontvangen deze handige voordelen!", "subscribersReceiveBenefits": "Abonnees ontvangen deze handige voordelen!",
"subCanceledTitle": "Abonnement opgezegd", "subCanceledTitle": "Abonnement opgezegd",
"backgroundAlreadyOwned": "Achtergrond al in bezit." "backgroundAlreadyOwned": "Achtergrond al in bezit.",
"mysterySet202004": "Machtige Vorsten Set"
} }

View file

@ -513,5 +513,12 @@
"backgroundButterflyGardenText": "Jardim de borboleta", "backgroundButterflyGardenText": "Jardim de borboleta",
"backgroundAmongGiantFlowersNotes": "Divirta-se entre flores gigantes.", "backgroundAmongGiantFlowersNotes": "Divirta-se entre flores gigantes.",
"backgroundAmongGiantFlowersText": "Entre flores gigantes", "backgroundAmongGiantFlowersText": "Entre flores gigantes",
"backgrounds032020": "Conjunto 70: lançado em Março de 2020" "backgrounds032020": "Conjunto 70: lançado em Março de 2020",
"backgroundRainyBarnyardNotes": "Encharque-se enquanto faz um passeio por um curral chuvoso.",
"backgroundRainyBarnyardText": "Curral chuvoso",
"backgroundHeatherFieldNotes": "Aproveite o perfume de um campo de urzes.",
"backgroundHeatherFieldText": "Campo de urzes",
"backgroundAnimalCloudsNotes": "Exercite sua imaginação encontrando formas de animais nas nuvens.",
"backgroundAnimalCloudsText": "Nuvens de animais",
"backgrounds042020": "Conjunto 71: lançado em Abril de 2020"
} }

View file

@ -2066,15 +2066,15 @@
"headSpecialSpring2020RogueNotes": "Tão vibrante e valioso que você será tentado a roubá-lo de sua própria cabeça. Aumenta a percepção em <% = per %>. Equipamento de edição limitada da primavera de 2020.", "headSpecialSpring2020RogueNotes": "Tão vibrante e valioso que você será tentado a roubá-lo de sua própria cabeça. Aumenta a percepção em <% = per %>. Equipamento de edição limitada da primavera de 2020.",
"armorSpecialSpring2020HealerNotes": "Envolva-se em folhas e pétalas de íris macias para enganar os inimigos e subestimar o seu poder de cura. Aumenta a Constituição em <%= con %>. Equipamento de edição limitada da primavera de 2020.", "armorSpecialSpring2020HealerNotes": "Envolva-se em folhas e pétalas de íris macias para enganar os inimigos e subestimar o seu poder de cura. Aumenta a Constituição em <%= con %>. Equipamento de edição limitada da primavera de 2020.",
"armorSpecialSpring2020HealerText": "Pétalas de proteção", "armorSpecialSpring2020HealerText": "Pétalas de proteção",
"armorSpecialSpring2020MageNotes": "Se você não consegue resistir a pisar nos restos das tempestades, esta armadura é para você! Transforme um impulso infantil em uma exibição de arte mística. Aumenta a Inteligência em <%= int%>. Equipamento de edição limitada da primavera de 2020.", "armorSpecialSpring2020MageNotes": "Se você não consegue resistir a pisar nos restos das tempestades, esta armadura é para você! Transforme um impulso infantil em uma exibição de arte mística. Aumenta a Inteligência em <%= int %>. Equipamento de edição limitada da primavera de 2020.",
"armorSpecialSpring2020MageText": "Vestido de redemoinho", "armorSpecialSpring2020MageText": "Vestido de redemoinho",
"armorSpecialSpring2020WarriorNotes": "Essa carapaça rígida pode mantê-lo seguro até dos ataques mais esmagadores. Aumenta a Constituição em <%= con %>. Equipamento de edição limitada da primavera de 2020.", "armorSpecialSpring2020WarriorNotes": "Essa carapaça rígida pode mantê-lo seguro até dos ataques mais esmagadores. Aumenta a Constituição em <%= con %>. Equipamento de edição limitada da primavera de 2020.",
"armorSpecialSpring2020WarriorText": "Armadura de exoesqueleto", "armorSpecialSpring2020WarriorText": "Armadura de exoesqueleto",
"armorSpecialSpring2020RogueNotes": "A cor do crepúsculo, de uma infinidade de pedras preciosas, do mar mais profundo! Aumenta a Percepção em <%= per %>. Equipamento de edição limitada da primavera de 2020.", "armorSpecialSpring2020RogueNotes": "A cor do crepúsculo, de uma infinidade de pedras preciosas, do mar mais profundo! Aumenta a Percepção em <%= per %>. Equipamento de edição limitada da primavera de 2020.",
"armorSpecialSpring2020RogueText": "Armadura ultramarina", "armorSpecialSpring2020RogueText": "Armadura ultramarina",
"weaponSpecialSpring2020HealerNotes": "Uma íris é linda, mas as folhas são como espadas ... não se deixe enganar pelas flores, esse cajado é duro como aço! Aumenta a Inteligência em <% =int %>. Equipamento de edição limitada da primavera de 2020.", "weaponSpecialSpring2020HealerNotes": "Uma íris é linda, mas as folhas são como espadas ... não se deixe enganar pelas flores, esse cajado é duro como aço! Aumenta a Inteligência em <%= int %>. Equipamento de edição limitada da primavera de 2020.",
"weaponSpecialSpring2020HealerText": "Cajado da espada-lírio", "weaponSpecialSpring2020HealerText": "Cajado da espada-lírio",
"weaponSpecialSpring2020MageNotes": "Eles continuam caindo em sua cabeça! Mas você nunca os impedirá reclamando. Aumenta a Inteligência em <%= int %> e a Percepção em <%=per %>. Equipamento de edição limitada da primavera de 2020.", "weaponSpecialSpring2020MageNotes": "Eles continuam caindo em sua cabeça! Mas você nunca os impedirá reclamando. Aumenta a Inteligência em <%= int %> e a Percepção em <%=per %>. Equipamento de edição limitada da primavera de 2020.",
"weaponSpecialSpring2020MageText": "Pingos de chuva", "weaponSpecialSpring2020MageText": "Pingos de chuva",
"weaponSpecialSpring2020WarriorNotes": "Lutar ou voar, esta asa irá te atender bem! Aumenta a Força em <%= str %>. Equipamento de edição limitada da primavera de 2020.", "weaponSpecialSpring2020WarriorNotes": "Lutar ou voar, esta asa irá te atender bem! Aumenta a Força em <%= str %>. Equipamento de edição limitada da primavera de 2020.",
"weaponSpecialSpring2020WarriorText": "Asa afiada", "weaponSpecialSpring2020WarriorText": "Asa afiada",
@ -2082,6 +2082,12 @@
"weaponSpecialSpring2020RogueText": "Lâmina de lazurita", "weaponSpecialSpring2020RogueText": "Lâmina de lazurita",
"headAccessoryMystery202004Notes": "Elas tremem um pouco se o perfume das flores passa perto -- use-as para encontrar um belo jardim! Não confere benefícios. Item de assinante, Abril de 2020.", "headAccessoryMystery202004Notes": "Elas tremem um pouco se o perfume das flores passa perto -- use-as para encontrar um belo jardim! Não confere benefícios. Item de assinante, Abril de 2020.",
"headAccessoryMystery202004Text": "Antenas do(a) Monarca poderoso(a)", "headAccessoryMystery202004Text": "Antenas do(a) Monarca poderoso(a)",
"backMystery202004Notes": "Faça um movimento rápido para o prado florido mais próximo ou migre pelo continente com essas lindas asas! Não confere benefícios. Item de assinante, Abril de 2020.", "backMystery202004Notes": "Faça um movimento rápido para o prado florido mais próximo ou migre pelo continente com essas lindas asas! Não confere benefícios. Item de assinante, Abril de 2020.",
"backMystery202004Text": "Asas do(a) Monarca poderoso(a)" "backMystery202004Text": "Asas do(a) Monarca poderoso(a)",
"shieldArmoireHobbyHorseNotes": "Monte seu belo cavalo do passatempo em direção às suas justas Recompensas! Aumenta Percepção e Constituição em <%= attrs %>, cada. Armário Encantado: Conjunto do Cavaleiro de papel (Item 2 de 3).",
"shieldArmoireHobbyHorseText": "Cavalo do passatempo",
"armorArmoireBoxArmorNotes": "Armadura de caixa de papelão: se couber, portanto, você senta... ops, portanto você o usa em batalha, como o cavaleiro ousado que você é! Aumenta Percepção e Constituição em <%= attrs %> , cada. Armário Encantado: Conjunto do Cavaleiro de papel (Item 3 de 3). ",
"armorArmoireBoxArmorText": "Armadura de caixa de papelão",
"weaponArmoirePaperCutterNotes": "Isso pode não parecer assustador, mas você nunca teve um cortador de papel? Aumenta Força em <%= str %>. Armário Encantado: Conjunto do Cavaleiro de papel (Item 1 de 3).",
"weaponArmoirePaperCutterText": "Cortador de papel"
} }

View file

@ -44,7 +44,7 @@
"hatchingPotion": "poção de eclosão", "hatchingPotion": "poção de eclosão",
"noHatchingPotions": "Você não possui poções de eclosão.", "noHatchingPotions": "Você não possui poções de eclosão.",
"inventoryText": "Clique num ovo para ver as poções utilizáveis destacadas em verde e depois clique em uma das poções destacadas para incubar seu mascote. Se nenhuma poção estiver destacada, clique no ovo novamente para desmarcá-lo, e em vez do ovo, clique na poção primeiro para ver os ovos utilizáveis marcados em verde. Você também pode vender itens indesejados para Alexander, o Comerciante.", "inventoryText": "Clique num ovo para ver as poções utilizáveis destacadas em verde e depois clique em uma das poções destacadas para incubar seu mascote. Se nenhuma poção estiver destacada, clique no ovo novamente para desmarcá-lo, e em vez do ovo, clique na poção primeiro para ver os ovos utilizáveis marcados em verde. Você também pode vender itens indesejados para Alexander, o Comerciante.",
"haveHatchablePet": "Você tem a Poção de Eclosão <%= potion %> e o ovo <%= egg %> para chocar este mascote! <b>Clique</b> na pata para chocar.", "haveHatchablePet": "Você tem a poção de eclosão <%= potion %> e o ovo <%= egg %> para chocar este mascote! <b>Clique</b>para chocar!",
"quickInventory": "Inventário Rápido", "quickInventory": "Inventário Rápido",
"foodText": "comida", "foodText": "comida",
"food": "Comida para mascotes e Selas", "food": "Comida para mascotes e Selas",

View file

@ -7,9 +7,9 @@
"noPhoto": "Acest Habitican nu și-a adăugat o poză.", "noPhoto": "Acest Habitican nu și-a adăugat o poză.",
"other": "Altele", "other": "Altele",
"fullName": "Numele complet", "fullName": "Numele complet",
"displayName": "Display name", "displayName": "Nume Afișat",
"changeDisplayName": "Change Display Name", "changeDisplayName": "Schimbă Numele Afișat",
"newDisplayName": "New Display Name", "newDisplayName": "Nume Afișat Nou",
"displayPhoto": "Poză", "displayPhoto": "Poză",
"displayBlurb": "Prezentare", "displayBlurb": "Prezentare",
"displayBlurbPlaceholder": "Te rugăm să te prezinți", "displayBlurbPlaceholder": "Te rugăm să te prezinți",
@ -47,7 +47,7 @@
"beard": "Barbă", "beard": "Barbă",
"mustache": "Mustață", "mustache": "Mustață",
"flower": "Floare", "flower": "Floare",
"accent": "Accent", "accent": "Accesorii",
"headband": "Bentiță", "headband": "Bentiță",
"wheelchair": "Scaun cu rotile", "wheelchair": "Scaun cu rotile",
"extra": "Altele", "extra": "Altele",
@ -80,7 +80,7 @@
"autoEquipPopoverText": "Selectează opțiunea de a îmbrăca echipament automat la cumpărarea acestuia.", "autoEquipPopoverText": "Selectează opțiunea de a îmbrăca echipament automat la cumpărarea acestuia.",
"costumeDisabled": "Ți-ai dezactivat costumul.", "costumeDisabled": "Ți-ai dezactivat costumul.",
"gearAchievement": "Ai câștigat trofeul „Armura Maximală” pentru că ai continuat să modernizezi până ai ajuns la setul maxim pentru o clasă! Ai obținut următoarele seturi complete:", "gearAchievement": "Ai câștigat trofeul „Armura Maximală” pentru că ai continuat să modernizezi până ai ajuns la setul maxim pentru o clasă! Ai obținut următoarele seturi complete:",
"gearAchievementNotification": "You have earned the \"Ultimate Gear\" Achievement for upgrading to the maximum gear set for a class!", "gearAchievementNotification": "Ați obținut Realizarea „Ultimate Gear” pentru actualizarea la maxim a setului de echipamentul pentru o clasă!",
"moreGearAchievements": "Pentru a obține mai multe insigne de Echipament Maximal, schimbă clasele din <a href='/user/settings/site' target='_blank'>Setări &gt; pagina Site-ului</a> și cumpără echipament pentru noua ta clasă!", "moreGearAchievements": "Pentru a obține mai multe insigne de Echipament Maximal, schimbă clasele din <a href='/user/settings/site' target='_blank'>Setări &gt; pagina Site-ului</a> și cumpără echipament pentru noua ta clasă!",
"armoireUnlocked": "Pentru mai mult echipament, verifică <strong>Cufărul Fermecat!</strong> Dă click pe Recompensa din Cufărul Fermecat pentru o șansă la un Echipament special random! E de asemenea posibil să primești aleator XP sau hrană.", "armoireUnlocked": "Pentru mai mult echipament, verifică <strong>Cufărul Fermecat!</strong> Dă click pe Recompensa din Cufărul Fermecat pentru o șansă la un Echipament special random! E de asemenea posibil să primești aleator XP sau hrană.",
"ultimGearName": "Echipament Maximal - <%= ultClass %>", "ultimGearName": "Echipament Maximal - <%= ultClass %>",
@ -104,7 +104,7 @@
"allocatePerPop": "Adaugă un Punct la Percepție", "allocatePerPop": "Adaugă un Punct la Percepție",
"allocateInt": "Puncte atribuite la INT:", "allocateInt": "Puncte atribuite la INT:",
"allocateIntPop": "Adaugă un Punct la Inteligență", "allocateIntPop": "Adaugă un Punct la Inteligență",
"noMoreAllocate": "Acum că ai atins nivelul 100, nu vei mai primi Puncte de Status suplimentare. Poți continua să îți crești nivelul, sau să începi o nouă aventură de la nivelul 1, folosind <a href='http://habitica.fandom.com/wiki/Orb_of_Rebirth' target='_blank'>Globul Renașterii</a>, acum disponibil gratis în Târg.", "noMoreAllocate": "Acum că ai atins nivelul 100, nu vei mai primi Puncte de Status suplimentare. Poți continua să îți crești nivelul, sau să începi o nouă aventură de la nivelul 1, folosind <a href='http://habitica.fandom.com/wiki/Orb_of_Rebirth' target='_blank'>Globul Renașterii</a>!",
"stats": "Status", "stats": "Status",
"achievs": "Realizari", "achievs": "Realizari",
"strength": "Forță", "strength": "Forță",
@ -156,7 +156,7 @@
"optOutOfClasses": "Refuză", "optOutOfClasses": "Refuză",
"optOutOfPMs": "Refuză", "optOutOfPMs": "Refuză",
"chooseClass": "Alege-ți Clasa", "chooseClass": "Alege-ți Clasa",
"chooseClassLearnMarkdown": "[Află mai multe despre sistemul de clase Habitica](http://habitica.fandom.com/wiki/Class_System)", "chooseClassLearnMarkdown": "[Află mai multe despre sistemul de clase Habitica](https://habitica.fandom.com/wiki/Class_System)",
"optOutOfClassesText": "Nu ai chef de clase? Vrei să alegi mai târziu? Refuză - vei fi un războinic fără abilități speciale. Poți citi despre sistemul de clase mai târziu pe wiki și poți activa clasele oricând din Icon-ul de Utilizator > Setări.", "optOutOfClassesText": "Nu ai chef de clase? Vrei să alegi mai târziu? Refuză - vei fi un războinic fără abilități speciale. Poți citi despre sistemul de clase mai târziu pe wiki și poți activa clasele oricând din Icon-ul de Utilizator > Setări.",
"selectClass": "Selectează <%= heroClass %>", "selectClass": "Selectează <%= heroClass %>",
"select": "Alege", "select": "Alege",
@ -185,7 +185,7 @@
"lostHealth": "Ai pierdut niște Sănătate", "lostHealth": "Ai pierdut niște Sănătate",
"lostExperience": "Ai pierdut niște Experiență", "lostExperience": "Ai pierdut niște Experiență",
"displayNameDescription1": "This is what appears in messages you post in the Tavern, guilds, and party chat, along with what is displayed on your avatar. To change it, click the Edit button above. If instead you want to change your username, go to", "displayNameDescription1": "This is what appears in messages you post in the Tavern, guilds, and party chat, along with what is displayed on your avatar. To change it, click the Edit button above. If instead you want to change your username, go to",
"displayNameDescription2": "Settings->Site", "displayNameDescription2": "Setări->Site",
"displayNameDescription3": "și uită-te la secțiunea de Înregistrare.", "displayNameDescription3": "și uită-te la secțiunea de Înregistrare.",
"unequipBattleGear": "Unequip Battle Gear", "unequipBattleGear": "Unequip Battle Gear",
"unequipCostume": "Unequip Costume", "unequipCostume": "Unequip Costume",
@ -224,5 +224,6 @@
"mainHand": "Mână dominantă", "mainHand": "Mână dominantă",
"offHand": "Mână nedominantă", "offHand": "Mână nedominantă",
"statPoints": "Stat Points", "statPoints": "Stat Points",
"pts": "pcte" "pts": "pcte",
"purchasePetItemConfirm": "Această achiziție ar depăși numărul de articole de care aveți nevoie pentru a ecloza toate animalele de companie <% = itemText%> posibile. Esti sigur?"
} }

View file

@ -1,8 +1,8 @@
{ {
"lostAllHealth": "Ai rămas fără sănătate!", "lostAllHealth": "Ai rămas fără sănătate!",
"dontDespair": "Nu dispera", "dontDespair": "Nu dispera!",
"deathPenaltyDetails": "Ai pierdut un nivel, aurul și o piesă de echipament, dar le poți obține înapoi prin muncă susținută! Mult noroc -- o să fie bine.", "deathPenaltyDetails": "Ai pierdut un nivel, aurul și o piesă de echipament, dar le poți obține înapoi prin muncă susținută! Mult noroc -- o să fie bine.",
"refillHealthTryAgain": "Realimentează cu sănătate și încearcă din nou.", "refillHealthTryAgain": "Realimentează cu Sănătate și Încearcă din Nou",
"dyingOftenTips": "Se întâmplă des? <a href='http://habitica.fandom.com/wiki/Death_Mechanics#Strategies_for_Staying_Alive' target='_blank'>Aici sunt niște indicii!</a>", "dyingOftenTips": "Se întâmplă des? <a href='http://habitica.fandom.com/wiki/Death_Mechanics#Strategies_for_Staying_Alive' target='_blank'>Aici sunt niște indicii!</a>",
"losingHealthWarning": "Atenție - pierzi din sănătate!", "losingHealthWarning": "Atenție - pierzi din sănătate!",
"losingHealthWarning2": "Nu lăsa sănătatea să scadă la zero! Dacă faci asta, vei pierde un nivel, tot aurul și o piesă de echipament.", "losingHealthWarning2": "Nu lăsa sănătatea să scadă la zero! Dacă faci asta, vei pierde un nivel, tot aurul și o piesă de echipament.",
@ -10,8 +10,8 @@
"lowHealthTips1": "Avansează un nivel pentru a te vindeca complet!", "lowHealthTips1": "Avansează un nivel pentru a te vindeca complet!",
"lowHealthTips2": "Cumpără o licoare de sănătate din Coloana de răsplăți pentru a restaura 15 puncte de sănătate.", "lowHealthTips2": "Cumpără o licoare de sănătate din Coloana de răsplăți pentru a restaura 15 puncte de sănătate.",
"losingHealthQuickly": "Pierzi sănătate prea rapid?", "losingHealthQuickly": "Pierzi sănătate prea rapid?",
"lowHealthTips3": "Sarcini zilnice necompletate te rănesc peste noapte, așa că ai grijă să nu adaugi prea multe la început.", "lowHealthTips3": "Sarcini zilnice necompletate te rănesc peste noapte, așa că ai grijă să nu adaugi prea multe la început!",
"lowHealthTips4": "Dacă o sarcină zilnică nu trebuie realizată ziua respectivă, o poți dezactiva printr-un clic pe iconița cu creion.", "lowHealthTips4": "Dacă o sarcină zilnică nu trebuie realizată ziua respectivă, o poți dezactiva printr-un clic pe iconița cu creion.",
"goodLuck": "Baftă!", "goodLuck": "Baftă!",
"cannotRevive": "Nu poți reînvia fără a fi mort" "cannotRevive": "Nu poți reînvia fără a fi mort"
} }

View file

@ -330,5 +330,6 @@
"signup": "Sign Up", "signup": "Sign Up",
"getStarted": "Get Started!", "getStarted": "Get Started!",
"mobileApps": "Mobile Apps", "mobileApps": "Mobile Apps",
"learnMore": "Learn More" "learnMore": "Learn More",
"communityInstagram": "Instagram"
} }

View file

@ -1,38 +1,38 @@
{ {
"tipTitle": "Pont #<%= tipNumber %>", "tipTitle": "Pont #<%= tipNumber %>",
"tip1": "Verifică-ți sarcinile departe de casă cu aplicațiile mobile Habitica.", "tip1": "Verifică-ți sarcinile departe de casă cu aplicațiile mobile Habitica.",
"tip2": "Click any equipment to see a preview, or equip it instantly by clicking the star in its upper-left corner!", "tip2": "Faceți clic pe orice echipament pentru a vedea o previzualizare sau echipați-l instantaneu făcând clic pe steaua din colțul din stânga sus!",
"tip3": "Puteți să vă personalizați sarcinile prin întrebuințarea ideogramelor emoji.", "tip3": "Puteți să vă personalizați sarcinile prin întrebuințarea ideogramelor emoji.",
"tip4": "Scrie simbolul # înaintea denumirii unei sarcini, și aceasta va avea un font uriaș!", "tip4": "Scrie simbolul # înaintea denumirii unei sarcini, și aceasta va avea un font uriaș!",
"tip5": "Este recomandat să folosești abilitățile dimineața, pentru a le face să dureze mai mult.", "tip5": "Este recomandat să folosești abilitățile dimineața, pentru a le face să dureze mai mult.",
"tip6": "Hover over a task and click the dots to access advanced task controls, such as the ability to push tasks to the top/bottom of your list.", "tip6": "Treceți peste o sarcină și faceți clic pe puncte pentru a accesa controale avansate ale sarcinilor, cum ar fi posibilitatea de a muta sarcinile la începutul/sfârșitul listei.",
"tip7": "Some backgrounds connect perfectly if Party members use the same background. Ex: Mountain Lake, Pagodas, and Rolling Hills.", "tip7": "Unele fundaluri se conectează perfect dacă membrii Echipei folosesc același fundal. Ex: Lacul de Munte, Pagode și Dealuri Domoale.",
"tip8": "Trimite un Mesaj cuiva dând click pe numele lor în chat și apoi apăsând pe pictograma cu plicul din susul profilului lor!", "tip8": "Trimite un Mesaj cuiva dând click pe numele lor în chat și apoi apăsând pe pictograma cu plicul din susul profilului lor!",
"tip9": "Use the filters + search bar in the Inventories, Shops, Guilds, and Challenges to quickly find what you want.", "tip9": "Utilizați filtrele + bara de căutare din Inventar, Magazine, Guilde și Provocări pentru a găsi rapid ceea ce doriți.",
"tip10": "Poți câștiga nestemate luând parte la Provocări. Acestea sunt adăugate în fiecare zi!", "tip10": "Poți câștiga nestemate luând parte la Provocări. Acestea sunt adăugate în fiecare zi!",
"tip11": "Having more than four Party members increases accountability!", "tip11": "A avea mai mult de patru membri în Echipă crește responsabilitatea!",
"tip12": "Adaugă liste de bifare la Activitățile tale De-Făcut pentru a-ți multiplica recompensele!", "tip12": "Adaugă liste de bifare la Activitățile tale De-Făcut pentru a-ți multiplica recompensele!",
"tip13": "Apasă pe ”Etichete” pe pagina ta cu sarcini pentru a transforma o listă cu sarcini greu de controlat într-una foarte controlabilă!", "tip13": "Apasă pe ”Etichete” pe pagina ta cu sarcini pentru a transforma o listă cu sarcini greu de controlat într-una foarte controlabilă!",
"tip14": "You can add headers or inspirational quotes to your list as Habits with no (+/-).", "tip14": "Puteți adăuga antete sau citate de inspirație la lista dvs. ca Obiceiuri fără (+/-).",
"tip15": "Complete all the Masterclasser Quest-lines to learn about Habiticas secret lore.", "tip15": "Completați toată linia de Aventuri Masterclasser pentru a afla mai multe despre secretele din Habitica.",
"tip16": "Click the link to the Data Display Tool in the footer for valuable insights on your progress.", "tip16": "Faceți clic pe linkul Unealtă de Afișare a Datelor din subsol pentru informații importante despre progresul dvs.",
"tip17": "Use the mobile apps to set reminders for your tasks.", "tip17": "Utilizați aplicațiile mobile pentru a seta mementouri pentru sarcinile dvs.",
"tip18": "Habits that are just positive or just negative gradually “fade” and return to yellow.", "tip18": "Obiceiurile care sunt doar pozitive sau doar negative se „estompează” treptat și revin la galben.",
"tip19": "Boost your Intelligence Stat to gain more experience when you complete a task.", "tip19": "Îmbunătățește-ți Statul de Informații pentru a câștiga mai multă experiență atunci când finalizați o sarcină.",
"tip20": "Boost your Perception Stat to get more drops and gold.", "tip20": "Îmbunătățește-ți Statul de Percepție pentru a obține mai multe recompense și aur.",
"tip21": "Boost your Strength Stat to do more boss damage or get critical hits.", "tip21": "Îmbunătățește-ți Statul de Forță pentru a face mai multe daune Căpcăunului sau a primi lovituri critice.",
"tip22": "Boost your Constitution Stat to lessen the damage from incomplete Dailies.", "tip22": "Îmbunătățește-ți Statul de Constituție pentru a reduce daunele cauzate de Cotidienele incomplete.",
"tip23": "Reach level 100 to unlock the Orb of Rebirth for free and start a new adventure!", "tip23": "Atinge nivelul 100 pentru a debloca gratuit Globului Renașterii și începe o nouă aventură!",
"tip24": "Have a question? Ask in the Habitica Help Guild!", "tip24": "Ai o intrebare? Întreabă în Ghilda Habitica Help!",
"tip25": "The four seasonal Grand Galas start near the solstices and equinoxes.", "tip25": "Cele patru Grand Galas sezoniere încep în apropierea solstițiilor și echinocțiilor.",
"tip26": "You can look for a Party or find Party members in the Party Wanted Guild!", "tip26": "Puteți căuta o Echipă sau puteți găsi membri ai Echipei în Ghildul Party Wanted!",
"tip27": "Did a Daily yesterday, but forgot to check it off? Don't worry! With Record Yesterday's Activity, you'll have a chance to record what you did before starting your new day.", "tip27": "Ai făcut o Cotidiană ieri, dar ai uitat să o înregistrezi? Nu-ți face griji! Cu Înregistrați Activitatea de Ieri, vei avea șansa să înregistrezi ceea ce ai făcut înainte de a începe noua ta zi.",
"tip28": "Set a Custom Day Start under User Icon > Settings to control when your day restarts.", "tip28": "Setați un Început de Zi Personalizat sub pictograma Utilizator > Setări pentru a controla când începe ziua.",
"tip29": "Complete all your Dailies to get a Perfect Day Buff that increases your Stats!", "tip29": "Completează toate Cotidienele tale pentru a obține un Spor Zi Perfectă care îți crește statisticile!",
"tip30": "You can invite people to Guilds, not just Parties.", "tip30": "Puteți invita oamenii în Ghildă, nu doar în Echipe.",
"tip31": "Check out the pre-made lists in the Library of Tasks and Challenges Guild for example tasks.", "tip31": "Consultați listele pre-făcute în Ghildul Library of Tasks and Challenges pentru exemple de sarcini.",
"tip32": "Lots of Habiticas code, art, and writing is made by volunteer contributors! Head to the Aspiring Legends Guild to help.", "tip32": "O mulțime de cod, artă și scriere de la Habitica sunt realizate de colaboratori voluntari! Mergeți spre Ghildul Aspiring Legends ca să ajutați.",
"tip33": "Check out The Bulletin Board Guild for news about Guilds, Challenges, and other player-created events - and announce your own there!", "tip33": "Vezi Ghildul The Bulletin Board pentru noutăți despre Ghilde, Provocările și alte evenimente create de jucători și anunță-l pe al tău acolo!",
"tip34": "Occasionally re-evaluate your tasks to make sure theyre up-to-date!", "tip34": "Re-evaluați ocazional sarcinile pentru a vă asigura că sunt la zi!",
"tip35": "Users who are part of a Group Plan gain the ability to assign tasks to other users in that Group for extra task management and accountability." "tip35": "Utilizatorii care fac parte dintr-un Plan de Grup câștigă capacitatea de a atribui sarcini altor utilizatori din Grupul respectiv pentru gestionare și responsabilitate suplimentară a sarcinilor."
} }

View file

@ -1,29 +1,29 @@
{ {
"unlockedReward": "Ai primit <%= reward %>", "unlockedReward": "Ai primit <%= reward %>",
"earnedRewardForDevotion": "You have earned <%= reward %> for being committed to improving your life.", "earnedRewardForDevotion": "Ai câștigat <%= reward %> pentru că te-ai comis să îți îmbunătățești viața.",
"nextRewardUnlocksIn": "Check-ins until your next prize: <%= numberOfCheckinsLeft %>", "nextRewardUnlocksIn": "Verificări până la următorul premiu: <%= numberOfCheckinsLeft %>",
"awesome": "Mișto!", "awesome": "Mișto!",
"totalCount": "<%= count %> total count", "totalCount": "<%= count %> numărătoare totală",
"countLeft": "Check-in-uri până la următoarea recompensă: <%= count %>", "countLeft": "Check-in-uri până la următoarea recompensă: <%= count %>",
"incentivesDescription": "Când vine vorba de formarea obiceiurilor, consistența este elementul cheie. Fiecare zi în care faceți check-in te va aduce mai aproape de un premiu. ", "incentivesDescription": "Când vine vorba de formarea obiceiurilor, consistența este elementul cheie. Fiecare zi în care faceți check-in te va aduce mai aproape de un premiu.",
"totalCheckins": "<%= count %>Check-in-uri", "totalCheckins": "<%= count %>Check-in-uri",
"checkinEarned": "Your Check-In Counter went up!", "checkinEarned": "Contorul tău de înregistrare a crescut!",
"unlockedCheckInReward": "Ai deblocat un Premiu pentru Check-In!", "unlockedCheckInReward": "Ai deblocat un Premiu pentru Check-In!",
"totalCheckinsTitle": "Check-In-uri totale", "totalCheckinsTitle": "Check-In-uri totale",
"checkinProgressTitle": "Progres până la următorul", "checkinProgressTitle": "Progres până la următorul",
"incentiveBackgroundsUnlockedWithCheckins": "Imaginile de Background simple blocate se vor debloca odată cu check-in-urile zilnice.", "incentiveBackgroundsUnlockedWithCheckins": "Imaginile de Background simple blocate se vor debloca odată cu check-in-urile zilnice.",
"checkinReceivedAllRewardsMessage": "Ai primit toate premiile disponibile pentru Check-In-uri! Felicitări!", "checkinReceivedAllRewardsMessage": "Ai primit toate premiile disponibile pentru Check-In-uri! Felicitări!",
"oneOfAllPetEggs": "Câte un Ou standard din fiecare", "oneOfAllPetEggs": "câte un Ou standard din fiecare",
"twoOfAllPetEggs": "two of each standard Pet Egg", "twoOfAllPetEggs": "câte două Ouă de Animale de Companie standard din fiecare",
"threeOfAllPetEggs": "three of each standard Pet Egg", "threeOfAllPetEggs": "câte trei Ouă de Animale de Companie standard din fiecare",
"oneOfAllHatchingPotions": "one of each standard Hatching Potion", "oneOfAllHatchingPotions": "câte o Poțiune de Eclozat standard din fiecare",
"threeOfEachFood": "three of each standard Pet Food", "threeOfEachFood": "câte trei Mâncare pentru Animale standard din fiecare",
"fourOfEachFood": "four of each standard Pet Food", "fourOfEachFood": "câte patru Mâncare pentru Animale standard din fiecare",
"twoSaddles": "two Saddles", "twoSaddles": "două Șei",
"threeSaddles": "three Saddles", "threeSaddles": "trei Șei",
"incentiveAchievement": "the Royally Loyal achievement", "incentiveAchievement": "realizarea Sincer Loial (the Royally Loyal)",
"royallyLoyal": "Royally Loyal", "royallyLoyal": "Sincer Loial",
"royallyLoyalText": "This user has checked in over 500 times, and has earned every Check-In Prize!", "royallyLoyalText": "Acest utilizator s-a înregistrat de peste 500 de ori și a obținut fiecare Premiu de Înregistrare!",
"checkInRewards": "Check-In Rewards", "checkInRewards": "Premiu de Înregistrare",
"backloggedCheckInRewards": "You received Check-In Prizes! Visit your Inventory and Equipment to see what's new." "backloggedCheckInRewards": "Ai primit Premii de Înregistrare! Accesează Inventarul și Echipamentul pentru a vedea ce este nou."
} }

View file

@ -1,20 +1,14 @@
{ {
"merch" : "Marfă", "merch": "Marfă",
"merchandiseDescription": "Looking for t-shirts, mugs, or stickers to show off your Habitica pride? Click here!", "merchandiseDescription": "Căutați tricouri, căni sau autocolante pentru a vă arăta mândria de la Habitica? Click aici!",
"merch-teespring-summary": "Teespring este o platforma care permit oricui să creeze și să vândă produse de înaltă calitate adorate de clienți, fără costuri sau riscuri asociate.",
"merch-teespring-summary" : "Teespring este o platforma care permit oricui să creeze și să vândă produse de înaltă calitate adorate de clienți, fără costuri sau riscuri asociate. ", "merch-teespring-goto": "Cumpără un tricou Habitica",
"merch-teespring-goto" : "Cumpără un tricou Habitica", "merch-teespring-mug-summary": "Teespring este o platformă care ușurează oricui să creeze și să vândă produse de înaltă calitate pe care le iubesc, fără costuri sau riscuri.",
"merch-teespring-mug-goto": "Cumpără o Cană Habitica",
"merch-teespring-mug-summary" : "Teespring is a platform that makes it easy for anyone to create and sell high-quality products people love, with no cost or risk.", "merch-teespring-eu-summary": "VERSIUNEA EUROPEANĂ: Teespring este o platformă care facilitează oricui să creeze și să vândă produse de înaltă calitate pe care le iubesc, fără costuri sau riscuri.",
"merch-teespring-mug-goto" : "Cumpără o Cană Habitica", "merch-teespring-eu-goto": "Cumpărați un tricou Habitica",
"merch-teespring-mug-eu-summary": "VERSIUNEA EUROPEANĂ: Teespring este o platformă care facilitează oricui să creeze și să vândă produse de înaltă calitate pe care le iubesc, fără costuri sau riscuri.",
"merch-teespring-eu-summary" : "EUROPEAN VERSION : Teespring is a platform that makes it easy for anyone to create and sell high-quality products people love, with no cost or risk.", "merch-teespring-mug-eu-goto": "Cumpără o Cană Habitica (UE)",
"merch-teespring-eu-goto" : "Cumpărați un tricou Habitica", "merch-stickermule-summary": "Lipiți mândru Melior oriunde voi (sau altcineva) aveți nevoie să vă aminti atât de realizările prezente, cât și de cele viitoare!",
"merch-stickermule-goto": "Cumpărați stickere Habitica"
"merch-teespring-mug-eu-summary" : "EUROPEAN VERSION : Teespring is a platform that makes it easy for anyone to create and sell high-quality products people love, with no cost or risk.",
"merch-teespring-mug-eu-goto" : "Cumpără o Cană Habitica (UE)",
"merch-stickermule-summary" : "Stick proud Melior wherever you (or someone else) need a reminder of both present and future accomplishments!",
"merch-stickermule-goto" : "Cumpărați stickere Habitica"
} }

View file

@ -1,59 +1,59 @@
{ {
"spellWizardFireballText": "Rafală de flăcări", "spellWizardFireballText": "Rafală de flăcări",
"spellWizardFireballNotes": "You summon XP and deal fiery damage to Bosses! (Based on: INT)", "spellWizardFireballNotes": "Convocați Experiență și faceți daune înflăcărate Căpcăunului! (Bazat pe: INT)",
"spellWizardMPHealText": "Val eteric", "spellWizardMPHealText": "Val eteric",
"spellWizardMPHealNotes": "You sacrifice Mana so the rest of your Party, except Mages, gains MP! (Based on: INT)", "spellWizardMPHealNotes": "Sacrifici Mana astfel încât restul Echipei tale, cu excepția Magilor, câștigă Puncte Mana! (Bazat pe: INT)",
"spellWizardEarthText": "Cutremur", "spellWizardEarthText": "Cutremur",
"spellWizardEarthNotes": "Your mental power shakes the earth and buffs your Party's Intelligence! (Based on: Unbuffed INT)", "spellWizardEarthNotes": "Puterea ta mentală zdruncină pământul și sporește Inteligența Echipei! (Bazat pe: INT Nesporită)",
"spellWizardFrostText": "Ger aprig", "spellWizardFrostText": "Ger aprig",
"spellWizardFrostNotes": "Cu o singură vrajă, toate șirurile tale sunt înghețate, astfel încât nu se vor mai reseta mâine!", "spellWizardFrostNotes": "Cu o singură vrajă, toate șirurile tale sunt înghețate, astfel încât nu se vor mai reseta mâine! ",
"spellWizardFrostAlreadyCast": "Ai folosit deja această vrajă astăzi. Șirurile tale sunt înghețate deja, deci nu este nevoie să faci aceeași vrajă din nou. ", "spellWizardFrostAlreadyCast": " Ai folosit deja această vrajă astăzi. Șirurile tale sunt înghețate deja, deci nu este nevoie să faci aceeași vrajă din nou.",
"spellWarriorSmashText": "Izbitură brutală", "spellWarriorSmashText": "Izbitură brutală",
"spellWarriorSmashNotes": "You make a task more blue/less red and deal extra damage to Bosses! (Based on: STR)", "spellWarriorSmashNotes": "Faceți o sarcină mai albastră/mai puțin roșie și faceți daune suplimentare Căpcăunului! (Bazat pe: FOR)",
"spellWarriorDefensiveStanceText": "Postură defensivă", "spellWarriorDefensiveStanceText": "Postură defensivă",
"spellWarriorDefensiveStanceNotes": "You crouch low and gain a buff to Constitution! (Based on: Unbuffed CON)", "spellWarriorDefensiveStanceNotes": "Vă ghemuți jos și obțineți un spor la Constituție! (Bazat pe: CON nesporit)",
"spellWarriorValorousPresenceText": "Tovărășie vitejească", "spellWarriorValorousPresenceText": "Tovărășie vitejească",
"spellWarriorValorousPresenceNotes": "Your boldness buffs your whole Party's Strength! (Based on: Unbuffed STR)", "spellWarriorValorousPresenceNotes": "Îndrăzneala ta îmbunătățește întreaga Forță a Echipei! (Bazat pe: Forța Nesporită)",
"spellWarriorIntimidateText": "Privire intimidantă", "spellWarriorIntimidateText": "Privire intimidantă",
"spellWarriorIntimidateNotes": "Your fierce stare buffs your whole Party's Constitution! (Based on: Unbuffed CON)", "spellWarriorIntimidateNotes": "Privirea ta înverșunată îmbunătățește întreaga Constituție a Echipei! (Bazat pe: CON nesporit)",
"spellRoguePickPocketText": "Pungaș", "spellRoguePickPocketText": "Pungaș",
"spellRoguePickPocketNotes": "You rob a nearby task and gain gold! (Based on: PER)", "spellRoguePickPocketNotes": "Tu jefuiești o sarcină din apropiere și câștigi aur! (Pe baza: PER)",
"spellRogueBackStabText": "Înjunghiere", "spellRogueBackStabText": "Înjunghiere",
"spellRogueBackStabNotes": "You betray a foolish task and gain gold and XP! (Based on: STR)", "spellRogueBackStabNotes": "Trădează o sarcină prostească și câștigi aur și XP! (Bazat pe: FOR)",
"spellRogueToolsOfTradeText": "Sculele meseriei", "spellRogueToolsOfTradeText": "Sculele meseriei",
"spellRogueToolsOfTradeNotes": "Your tricky talents buff your whole Party's Perception! (Based on: Unbuffed PER)", "spellRogueToolsOfTradeNotes": "Talentele tale complicate păstrează Percepția întregii Echipe! (Bazat pe: PER nesporit)",
"spellRogueStealthText": "Discreție", "spellRogueStealthText": "Discreție",
"spellRogueStealthNotes": "With each cast, a few of your undone Dailies won't cause damage tonight. Their streaks and colors won't change. (Based on: PER)", "spellRogueStealthNotes": "Cu fiecare aruncare a vrăjii, câteva dintre Cotidianele voastre nefăcute nu vor provoca daune în această seară. Șirulși culorile lor nu se vor schimba. (Pe baza: PER)",
"spellRogueStealthDaliesAvoided": "<%= originalText %> Number of dailies avoided: <%= number %>.", "spellRogueStealthDaliesAvoided": "<%= originalText %> Numumărul de Cotidiene evitate: <%= number %>.",
"spellRogueStealthMaxedOut": "You have already avoided all your dailies; there's no need to cast this again.", "spellRogueStealthMaxedOut": " You have already avoided all your dailies; there's no need to cast this again.",
"spellHealerHealText": "Lumină vindecătoare", "spellHealerHealText": "Lumină vindecătoare",
"spellHealerHealNotes": "Shining light restores your health! (Based on: CON and INT)", "spellHealerHealNotes": "Lumina strălucitoare îți restabilește sănătatea! (Bazat pe: CON și INT)",
"spellHealerBrightnessText": "Strălucire mistuitoare", "spellHealerBrightnessText": "Strălucire mistuitoare",
"spellHealerBrightnessNotes": "A burst of light makes your tasks more blue/less red! (Based on: INT)", "spellHealerBrightnessNotes": "O explozie de lumină îți face sarcinile mai albastre/mai puțin roșii! (Bazat pe: INT)",
"spellHealerProtectAuraText": "Aură protectoare", "spellHealerProtectAuraText": "Aură protectoare",
"spellHealerProtectAuraNotes": "You shield your Party by buffing their Constitution! (Based on: Unbuffed CON)", "spellHealerProtectAuraNotes": "Îți protejezi Echipa prin sporirea Constituției! (Bazat pe: CON nesporită)",
"spellHealerHealAllText": "Binecuvântare", "spellHealerHealAllText": "Binecuvântare",
"spellHealerHealAllNotes": "Your soothing spell restores your whole Party's health! (Based on: CON and INT)", "spellHealerHealAllNotes": "Vraja ta liniștitoare restabilește sănătatea întregii tale Echipe! (Bazat pe: CON și INT)",
"spellSpecialSnowballAuraText": "Bulgăre de zăpadă", "spellSpecialSnowballAuraText": "Bulgăre de zăpadă",
"spellSpecialSnowballAuraNotes": "Turn a friend into a frosty snowman!", "spellSpecialSnowballAuraNotes": "Transformă un prieten într-un om de zăpadă înghețat!",
"spellSpecialSaltText": "Sare", "spellSpecialSaltText": "Sare",
"spellSpecialSaltNotes": "Reverse the spell that made you a snowman.", "spellSpecialSaltNotes": "Inversează vraja care te-a făcut om de zăpadă.",
"spellSpecialSpookySparklesText": "Spooky Sparkles", "spellSpecialSpookySparklesText": "Fantoma Sclipicioasă",
"spellSpecialSpookySparklesNotes": "Turn your friend into a transparent pal!", "spellSpecialSpookySparklesNotes": "Transformă-ți prietenul într-un prieten transparent!",
"spellSpecialOpaquePotionText": "Poțiune opacă", "spellSpecialOpaquePotionText": "Poțiune opacă",
"spellSpecialOpaquePotionNotes": "Reverse the spell that made you transparent.", "spellSpecialOpaquePotionNotes": "Inversează vraja care te-a făcut transparent.",
"spellSpecialShinySeedText": "Shiny Seed", "spellSpecialShinySeedText": "Semința Strălucitoare",
"spellSpecialShinySeedNotes": "Turn a friend into a joyous flower!", "spellSpecialShinySeedNotes": "Transformă un prieten într-o floare veselă!",
"spellSpecialPetalFreePotionText": "Petal-Free Potion", "spellSpecialPetalFreePotionText": "Poțiune Fără Petale",
"spellSpecialPetalFreePotionNotes": "Reverse the spell that made you a flower.", "spellSpecialPetalFreePotionNotes": "Inversați vraja care v-a făcut floare.",
"spellSpecialSeafoamText": "Seafoam", "spellSpecialSeafoamText": "Spumă de Mare",
"spellSpecialSeafoamNotes": "Turn a friend into a sea creature!", "spellSpecialSeafoamNotes": "Transformă un prieten într-o creatură de mare!",
"spellSpecialSandText": "Sand", "spellSpecialSandText": "Nisip",
"spellSpecialSandNotes": "Reverse the spell that made you a sea star.", "spellSpecialSandNotes": "Inversează vraja care te-a făcut o stea de mare.",
"partyNotFound": "Party not found", "partyNotFound": "Echipa nu a fost găsită",
"targetIdUUID": "\"targetId\" must be a valid User ID.", "targetIdUUID": "\"targetId\" trebuie să fie un ID de utilizator valid.",
"challengeTasksNoCast": "Casting a skill on challenge tasks is not allowed.", "challengeTasksNoCast": "Aplicarea unei abilități în sarcinile de la provocări nu este permisă.",
"groupTasksNoCast": "Casting a skill on group tasks is not allowed.", "groupTasksNoCast": "Nu este permisă aplicarea unei abilități în sarcinile grupului.",
"spellNotOwned": "You don't own this skill.", "spellNotOwned": "Nu dețineți această abilitate.",
"spellLevelTooHigh": "You must be level <%= level %> to use this skill." "spellLevelTooHigh": "Pentru a utiliza această abilitate trebuie să fiți nivelul <%= level %>."
} }

View file

@ -6,7 +6,7 @@
"lotOfToDos": "Cele mai recente 30 de activități De-Făcut completate apar aici. Le poți vedea pe cele mai vechi din Data > Data Display Tool sau Data > Export Data > User Data.", "lotOfToDos": "Cele mai recente 30 de activități De-Făcut completate apar aici. Le poți vedea pe cele mai vechi din Data > Data Display Tool sau Data > Export Data > User Data.",
"deleteToDosExplanation": "Dacă apeși butonul de mai jos, toate activitățile tale De-Făcut completate și cele arhivate vor fi șterse permanent, cu excepția celor din provocări în desfășurare și din Planurile de Grup. Exportă-le întâi dacă vrei să ții o evidență a lor.", "deleteToDosExplanation": "Dacă apeși butonul de mai jos, toate activitățile tale De-Făcut completate și cele arhivate vor fi șterse permanent, cu excepția celor din provocări în desfășurare și din Planurile de Grup. Exportă-le întâi dacă vrei să ții o evidență a lor.",
"addMultipleTip": "<strong>Sfat</strong> Pentru a adăuga multiple <%= taskType %>, separă-le pe fiecare utilizând o linie de pauză (Shift + Enter) și apoi apasă ”Enter.”", "addMultipleTip": "<strong>Sfat</strong> Pentru a adăuga multiple <%= taskType %>, separă-le pe fiecare utilizând o linie de pauză (Shift + Enter) și apoi apasă ”Enter.”",
"addsingle": "Add Single", "addsingle": "Adaugă Individual",
"addATask": "Adaugă un/o <%= type %>", "addATask": "Adaugă un/o <%= type %>",
"editATask": "Editează un/o <%= type %>", "editATask": "Editează un/o <%= type %>",
"createTask": "Crează <%= type %>", "createTask": "Crează <%= type %>",
@ -16,7 +16,7 @@
"habit": "Obicei", "habit": "Obicei",
"habits": "Obiceiuri", "habits": "Obiceiuri",
"newHabit": "Obicei nou", "newHabit": "Obicei nou",
"newHabitBulk": "New Habits (one per line)", "newHabitBulk": "Obiceiuri noi (câte unul pe linie)",
"habitsDesc": "Obiceiurile nu au un program rigid. Le poți bifa de mai multe ori pe zi.", "habitsDesc": "Obiceiurile nu au un program rigid. Le poți bifa de mai multe ori pe zi.",
"positive": "Positiv", "positive": "Positiv",
"negative": "Negativ", "negative": "Negativ",
@ -30,7 +30,7 @@
"newChecklistItem": "Obiect nou în listă", "newChecklistItem": "Obiect nou în listă",
"expandChecklist": "Extinde Lista", "expandChecklist": "Extinde Lista",
"collapseChecklist": "Pliază Checklist-ul", "collapseChecklist": "Pliază Checklist-ul",
"text": "Title", "text": "Titlu",
"extraNotes": "Alte note", "extraNotes": "Alte note",
"notes": "Notițe", "notes": "Notițe",
"direction/Actions": "Direcție/Acțiuni", "direction/Actions": "Direcție/Acțiuni",
@ -41,7 +41,7 @@
"taskAliasPopoverWarning": "ATENȚIE: Schimbarea acestei valor iva schimba orice integrare a unei partiții terțe care se bazează pe acest alias sarcină.", "taskAliasPopoverWarning": "ATENȚIE: Schimbarea acestei valor iva schimba orice integrare a unei partiții terțe care se bazează pe acest alias sarcină.",
"difficulty": "Dificultate", "difficulty": "Dificultate",
"difficultyHelp": "Dificultatea descrie cât de solicitantă este completarea unui Obicei, a unei Cotidiene sau a unui lucru De-Făcut. Cu cât dificultatea este mai înaltă, cu atât recompensele de completare a sarcinii vor fi mai mari, dar de asemenea vor determina deteriorare mai mare atunci când o Cotidiană este ratată sau un Obicei negativ este apăsat.", "difficultyHelp": "Dificultatea descrie cât de solicitantă este completarea unui Obicei, a unei Cotidiene sau a unui lucru De-Făcut. Cu cât dificultatea este mai înaltă, cu atât recompensele de completare a sarcinii vor fi mai mari, dar de asemenea vor determina deteriorare mai mare atunci când o Cotidiană este ratată sau un Obicei negativ este apăsat.",
"trivial": "Trivial", "trivial": "Banal",
"easy": "Ușor", "easy": "Ușor",
"medium": "Mediu", "medium": "Mediu",
"hard": "Greu", "hard": "Greu",
@ -52,20 +52,20 @@
"daily": "Activitate Zilnică", "daily": "Activitate Zilnică",
"dailies": "Cotidiene", "dailies": "Cotidiene",
"newDaily": "Cotidiană nouă", "newDaily": "Cotidiană nouă",
"newDailyBulk": "New Dailies (one per line)", "newDailyBulk": "O nouă activitate zilnică (câte una pe fiecare linie)",
"dailysDesc": "Cotidienele se repetă regulat. Alege-ți programul care funcționează cel mai bine pentru tine!", "dailysDesc": "Cotidienele se repetă regulat. Alege-ți programul care funcționează cel mai bine pentru tine!",
"streakCounter": "Contor șir", "streakCounter": "Contor șir",
"repeat": "Repetă", "repeat": "Repetă",
"repeats": "Se repetă", "repeats": "Se repetă",
"repeatEvery": "Repeat Every", "repeatEvery": "Repetă la fiecare",
"repeatOn": "Se repetă pe", "repeatOn": "Se repetă pe",
"repeatHelpTitle": "How often should this task be repeated?", "repeatHelpTitle": "Cât de des ar trebui să se repete această sarcină?",
"dailyRepeatHelpContent": "This task will be due every X days. You can set that value below.", "dailyRepeatHelpContent": "Această sarcină se va efectua la fiecare X zile. Poți seta această valoare mai jos.",
"weeklyRepeatHelpContent": "This task will be due on the highlighted days below. Click on a day to activate/deactivate it.", "weeklyRepeatHelpContent": "Această sarcină se va efectua în zilele evidențiate mai jos. Faceți clic pe o zi pentru a o activa/dezactiva.",
"repeatDays": "Every X Days", "repeatDays": "La fiecare X Zile",
"repeatWeek": "On Certain Days of the Week", "repeatWeek": "În anumite zile ale săptămânii",
"day": "Day", "day": "Day",
"days": "Days", "days": "Zile",
"restoreStreak": "Ajustează Șirul", "restoreStreak": "Ajustează Șirul",
"resetStreak": "Resetează Șirul", "resetStreak": "Resetează Șirul",
"todo": "De-Făcut", "todo": "De-Făcut",
@ -75,23 +75,23 @@
"todosDesc": "Lucrurile De-Făcut trebuie completate o singură dată. Adaugă o bife acestora pentru a le crește valoarea.", "todosDesc": "Lucrurile De-Făcut trebuie completate o singură dată. Adaugă o bife acestora pentru a le crește valoarea.",
"dueDate": "Dată limită", "dueDate": "Dată limită",
"remaining": "Active", "remaining": "Active",
"complete": "Done", "complete": "Terminat",
"complete2": "Complet", "complete2": "Complet",
"dated": "Dated", "dated": "Datat",
"today": "Astăzi", "today": "Astăzi",
"dueIn": "Termen limită <%= dueIn %>", "dueIn": "Termen limită <%= dueIn %>",
"due": "Scadente", "due": "Scadente",
"notDue": "Not Due", "notDue": "Realizate",
"grey": "Gri", "grey": "Gri",
"score": "Score", "score": "Score",
"reward": "Recompensă", "reward": "Recompensă",
"rewards": "Recompense", "rewards": "Recompense",
"rewardsDesc": "Recompensele sunt o modalitate grozavă de a utiliza Habitica și a-ți completa sarcinile. Încearcă să adaugi câteva astăzi!", "rewardsDesc": "Recompensele sunt o modalitate grozavă de a utiliza Habitica și a-ți completa sarcinile. Încearcă să adaugi câteva astăzi!",
"ingamerewards": "Equipment & Skills", "ingamerewards": "Echipament & Abilități",
"gold": "Aur", "gold": "Aur",
"silver": "Argint (100 Arginți = 1 Aur)", "silver": "Argint (100 Arginți = 1 Aur)",
"newReward": "Răsplată nouă", "newReward": "Răsplată nouă",
"newRewardBulk": "New Rewards (one per line)", "newRewardBulk": "Recompense noi (una pe linie)",
"price": "Preț", "price": "Preț",
"tags": "Etichete", "tags": "Etichete",
"editTags": "Modifică", "editTags": "Modifică",
@ -102,13 +102,13 @@
"editTags2": "Editează Etichetele", "editTags2": "Editează Etichetele",
"toRequired": "Trebuie să oferi o caracteristică ”to”", "toRequired": "Trebuie să oferi o caracteristică ”to”",
"startDate": "Start Date", "startDate": "Start Date",
"startDateHelpTitle": "When should this task start?", "startDateHelpTitle": "Când ar trebui să înceapă această sarcină?",
"startDateHelp": "Set the date for which this task takes effect. Will not be due on earlier days.", "startDateHelp": "Setați data pentru care această sarcină are efect. Nu va fi scadent în zilele anterioare.",
"streaks": "Realizări legate de Șir", "streaks": "Realizări legate de Șir",
"streakName": "<%= count %> Realizări legate de Șir", "streakName": "<%= count %> Realizări legate de Șir",
"streakText": "A îndeplinit <%= count %> șiruri a câte 21 de zile în Cotidiene", "streakText": "A îndeplinit <%= count %> șiruri a câte 21 de zile în Cotidiene",
"streakSingular": "Înșiruitor", "streakSingular": "Înșiruitor",
"streakSingularText": "A îndeplinit 21 de zile în șir o Cotidiană", "streakSingularText": "A îndeplinit 21 de zile în șir o Cotidiană",
"perfectName": "<%= count %> Zile Perfecte", "perfectName": "<%= count %> Zile Perfecte",
"perfectText": "A completat toate Cotidienele în <%= count %> zile. Cu această realizare primești +nivel/2 sporuri pentru toate atributele în ziua următoare. Nivelele mai mari de 100 nu au niciun spor suplimentar.", "perfectText": "A completat toate Cotidienele în <%= count %> zile. Cu această realizare primești +nivel/2 sporuri pentru toate atributele în ziua următoare. Nivelele mai mari de 100 nu au niciun spor suplimentar.",
"perfectSingular": "Zi perfectă", "perfectSingular": "Zi perfectă",
@ -125,25 +125,25 @@
"streakCoins": "Bonus de realizare în șir!", "streakCoins": "Bonus de realizare în șir!",
"taskToTop": "Mergi sus", "taskToTop": "Mergi sus",
"taskToBottom": "Mergi jos", "taskToBottom": "Mergi jos",
"emptyTask": "Enter the task's title first.", "emptyTask": "Introduceți mai întâi titlul sarcinii.",
"dailiesRestingInInn": "You're Resting in the Inn! Your Dailies will NOT hurt you tonight, but they WILL still refresh every day. If you're in a quest, you won't deal damage/collect items until you check out of the Inn, but you can still be injured by a Boss if your Party mates skip their own Dailies.", "dailiesRestingInInn": "Te odihnești în han! Cotidienele tale NU te vor răni în această seară, dar ele SE VOR reînnoi în fiecare zi. Dacă ești într-o aventură, nu vei suferi daune/colecta obiecte până nu pleci de la Han, dar poți fi în continuare rănit de un Căpcăun dacă membrii Echipei tale sar peste propriile Cotidiene.",
"habitHelp1": "Good Habits are things that you do often. They award Gold and Experience every time you click the <%= plusIcon %>.", "habitHelp1": "Obiceiuri Bune sunt lucruri pe care le faci des. Ele acordă Aur și Experiență de fiecare dată când faceți clic pe <%= plusIcon %>.",
"habitHelp2": "Bad Habits are things you want to avoid doing. They remove Health every time you click the <%= minusIcon %>.", "habitHelp2": "Obiceiurile Rele sunt lucruri pe care doriți să le evitați. Acestea elimină Sănătate de fiecare dată când faceți clic pe <%= minusIcon %>.",
"habitHelp3": "For inspiration, check out these <a href='http://habitica.fandom.com/wiki/Sample_Habits' target='_blank'>sample Habits</a>!", "habitHelp3": "Pentru inspirație, consultați aceste <a href='http://habitica.fandom.com/wiki/Sample_Habits' target='_blank'>exemple de Obiceiuri</a>!",
"newbieGuild": "Ai alte întrebări? Întreabă în <%= linkStart %> Habitica Help Guild<%= linkEnd %>!", "newbieGuild": "Ai alte întrebări? Întreabă în <%= linkStart %> Habitica Help Guild<%= linkEnd %>!",
"dailyHelp1": "Dailies repeat <%= emphasisStart %>every day<%= emphasisEnd %> that they are active. Click the <%= pencilIcon %> to change the days a Daily is active.", "dailyHelp1": "Zilnicele repetă <%= emphasisStart %> în fiecare zi<%= emphasisEnd %> că sunt active. Faceți clic pe <%= pencilIcon %> pentru a schimba zilele în care sunt activate Zilnicele.",
"dailyHelp2": "If you don't complete active Dailies, you lose Health when your day rolls over.", "dailyHelp2": "Dacă nu completați Zilnicele active, pierdeți Sănătate atunci când ziua ta se va termina.",
"dailyHelp3": "Dailies turn <%= emphasisStart %>redder<%= emphasisEnd %> when you miss them, and <%= emphasisStart %>bluer<%= emphasisEnd %> when you complete them. The redder the Daily, the more it will reward you... or hurt you.", "dailyHelp3": "Cotidienele devin <%= emphasisStart %>mai roși<%= emphasisEnd %> când le lipsești și <%= emphasisStart %>mai albastre<%= emphasisEnd %> când le completezi. Cu cât este mai roșu Cotidianul, cu atât te va răsplăti mai mult ... sau te va răni.",
"dailyHelp4": "To change when your day rolls over, go to <%= linkStart %> Settings > Site<%= linkEnd %> > Custom Day Start.", "dailyHelp4": "Pentru a vă schimba când ziua dvs. se va trece, mergeți la <%= linkStart %> Setări > Site<%= linkEnd %> > Când Începe Ziua.",
"dailyHelp5": "For inspiration, check out these <a href='http://habitica.fandom.com/wiki/Sample_Dailies' target='_blank'>sample Dailies</a>!", "dailyHelp5": "Pentru inspirație, verificați acestea <a href='http://habitica.fandom.com/wiki/Sample_Dailies' target='_blank'>exemple de Cotidiene (sau Zilnice)</a>!",
"toDoHelp1": "To-Dos start yellow, and get redder (more valuable) the longer it takes to complete them.", "toDoHelp1": "Sarcinile De-Făcut încep galbene și devine mai roșu (mai valoroase) cu cât durează mai mult pentru a le completa.",
"toDoHelp2": "To-Dos never hurt you! They only award Gold and Experience.", "toDoHelp2": "Sarcinile De-Făcut nu te rănesc niciodată! Ele acordă doar Aur și Experiență.",
"toDoHelp3": "Breaking a To-Do down into a checklist of smaller items will make it less scary, and will increase your points!", "toDoHelp3": "Spargerea unei sarcini De-Făcut într-o listă cu articole mai mici o va face mai puțin înfricoșător și vă va crește punctele!",
"toDoHelp4": "For inspiration, check out these <a href='http://habitica.fandom.com/wiki/Sample_To-Dos' target='_blank'>sample To-Dos</a>!", "toDoHelp4": "Pentru inspirație, verificați acestea <a href='http://habitica.fandom.com/wiki/Sample_To-Dos' target='_blank'>exemple de Sarcini De-Făcut</a>!",
"rewardHelp1": "The Equipment you buy for your avatar is stored in <%= linkStart %>Inventory > Equipment<%= linkEnd %>.", "rewardHelp1": "Echipamentul pe care îl cumperi pentru avatarul tău este depozitat în <%= linkStart %>Inventar > Echipament<%= linkEnd %>.",
"rewardHelp2": "Echipamentul îți influențează Statisticile (<%= linkStart %>Avatar > Stats<%= linkEnd %>).", "rewardHelp2": "Echipamentul îți influențează Statisticile (<%= linkStart %>Avatar > Stats<%= linkEnd %>).",
"rewardHelp3": "Special equipment will appear here during World Events.", "rewardHelp3": "Echipamente speciale vor apărea aici în timpul Evenimentelor Mondiale.",
"rewardHelp4": "Don't be afraid to set custom Rewards! Check out <a href='http://habitica.fandom.com/wiki/Sample_Custom_Rewards' target='_blank'>some samples here</a>.", "rewardHelp4": "Nu vă fie teamă să setați Recompense personalizate! Verifică <a href='http://habitica.fandom.com/wiki/Sample_Custom_Rewards' target='_blank'>câteva exemple aici</a>.",
"clickForHelp": "Click for help", "clickForHelp": "Click for help",
"taskAliasAlreadyUsed": "Aliasul sarcinii este deja utilizat pentru o alta.", "taskAliasAlreadyUsed": "Aliasul sarcinii este deja utilizat pentru o alta.",
"taskNotFound": "Sarcina nu a putut fi găsită.", "taskNotFound": "Sarcina nu a putut fi găsită.",
@ -173,7 +173,7 @@
"habitCounterDown": "Counter Negativ (Se resetează <%= frequency %>)", "habitCounterDown": "Counter Negativ (Se resetează <%= frequency %>)",
"taskRequiresApproval": "Această sarcină trebuie aprobată înainte să o poți completa. Aprobarea a fost deja solicitată", "taskRequiresApproval": "Această sarcină trebuie aprobată înainte să o poți completa. Aprobarea a fost deja solicitată",
"taskApprovalHasBeenRequested": "Aprobarea a fost solicitată", "taskApprovalHasBeenRequested": "Aprobarea a fost solicitată",
"taskApprovalWasNotRequested": "Numai o sarcină așteaptă aprobare și poate fi marcată ca necesitând treabă suplimentară", "taskApprovalWasNotRequested": "Nu a fost solicitată aprobarea pentru această sarcină.",
"approvals": "Aprobări", "approvals": "Aprobări",
"approvalRequired": "Necesită Aprobare", "approvalRequired": "Necesită Aprobare",
"repeatZero": "Cotidenele nu au niciodată termen limită", "repeatZero": "Cotidenele nu au niciodată termen limită",

View file

@ -504,5 +504,7 @@
"backgroundHallOfHeroesText": "Зал героев", "backgroundHallOfHeroesText": "Зал героев",
"backgrounds022020": "Набор 69: Выпущен в феврале 2020", "backgrounds022020": "Набор 69: Выпущен в феврале 2020",
"backgroundElegantBallroomText": "Элегантный бальный зал", "backgroundElegantBallroomText": "Элегантный бальный зал",
"backgrounds032020": "Набор 70: Выпущен в марте 2020" "backgrounds032020": "Набор 70: Выпущен в марте 2020",
"backgroundSucculentGardenText": "Суккулентный сад",
"backgroundButterflyGardenText": "Сад бабочек"
} }

View file

@ -2038,5 +2038,7 @@
"weaponArmoireLivelyMatchText": "Искрящиеся спичка", "weaponArmoireLivelyMatchText": "Искрящиеся спичка",
"shieldArmoireBaseballGloveText": "Бейсбольная перчатка", "shieldArmoireBaseballGloveText": "Бейсбольная перчатка",
"headArmoireBaseballCapText": "Бейсболка", "headArmoireBaseballCapText": "Бейсболка",
"weaponArmoireBaseballBatText": "Бейсбольная бита" "weaponArmoireBaseballBatText": "Бейсбольная бита",
"armorSpecialSpring2020RogueText": "Ультрамариновые доспехи",
"weaponSpecialSpring2020MageText": "Капли дождя"
} }

View file

@ -90,40 +90,40 @@
"grandMalkinSet": "Великий Малкин (Маг)", "grandMalkinSet": "Великий Малкин (Маг)",
"cleverDogSet": "Мудрый Пёс (Вор)", "cleverDogSet": "Мудрый Пёс (Вор)",
"braveMouseSet": "Храбрая мышь (Воин)", "braveMouseSet": "Храбрая мышь (Воин)",
"summer2016SharkWarriorSet": "Воин-акула (Воин)", "summer2016SharkWarriorSet": "Акула (Воин)",
"summer2016DolphinMageSet": "Магельфин (Маг)", "summer2016DolphinMageSet": "Дельфин (Маг)",
"summer2016SeahorseHealerSet": "Целитель - морской конёк (Целитель)", "summer2016SeahorseHealerSet": "Морской конёк (Целитель)",
"summer2016EelSet": "Разбойник-угорь (Разбойник)", "summer2016EelSet": "Угорь (Разбойник)",
"fall2016SwampThingSet": "Болотная тварь (Воин)", "fall2016SwampThingSet": "Болотная тварь (Воин)",
"fall2016WickedSorcererSet": "Злой колдун (Маг)", "fall2016WickedSorcererSet": "Злой колдун (Маг)",
"fall2016GorgonHealerSet": "Горгона-целитель (Целитель)", "fall2016GorgonHealerSet": "Горгона (Целитель)",
"fall2016BlackWidowSet": "Черная вдова-разбойница (Разбойник)", "fall2016BlackWidowSet": "Черная вдова (Разбойник)",
"winter2017IceHockeySet": "Хоккейная клюшка (воин)", "winter2017IceHockeySet": "Хоккейная клюшка (воин)",
"winter2017WinterWolfSet": "Зимний волк (Маг)", "winter2017WinterWolfSet": "Зимний волк (Маг)",
"winter2017SugarPlumSet": "Сахарная слива (Целитель)", "winter2017SugarPlumSet": "Сахарная слива (Целитель)",
"winter2017FrostyRogueSet": "Морозный разбойник (Разбойник)", "winter2017FrostyRogueSet": "Морозный разбойник (Разбойник)",
"spring2017FelineWarriorSet": "Кошачий воин (воин)", "spring2017FelineWarriorSet": "Кошачий (воин)",
"spring2017CanineConjurorSet": "Собачий заклинатель(маг)", "spring2017CanineConjurorSet": "Собачий заклинатель(маг)",
"spring2017FloralMouseSet": "Цветочная мышь (Целитель)", "spring2017FloralMouseSet": "Цветочная мышь (Целитель)",
"spring2017SneakyBunnySet": "Скрытный кролик (Разбойник)", "spring2017SneakyBunnySet": "Скрытный кролик (Разбойник)",
"summer2017SandcastleWarriorSet": "Воин песчаного замка (Воин)", "summer2017SandcastleWarriorSet": "Песчаный замок (Воин)",
"summer2017WhirlpoolMageSet": "Маг водоворота (Маг)", "summer2017WhirlpoolMageSet": "Водоворот (Маг)",
"summer2017SeashellSeahealerSet": "Целитель морской ракушки (Целитель)", "summer2017SeashellSeahealerSet": "Целитель морской ракушки (Целитель)",
"summer2017SeaDragonSet": "Морской дракон (Разбойник)", "summer2017SeaDragonSet": "Морской дракон (Разбойник)",
"fall2017HabitoweenSet": "Привычкоуинский воин", "fall2017HabitoweenSet": "Привычкоуин (Воин)",
"fall2017MasqueradeSet": "Маскарадный маг (Маг)", "fall2017MasqueradeSet": "Маскарад (Маг)",
"fall2017HauntedHouseSet": "Целитель Проклятого Дома (Целитель)", "fall2017HauntedHouseSet": "Проклятый Дом (Целитель)",
"fall2017TrickOrTreatSet": "Кошелек или жизнь (Разбойник)", "fall2017TrickOrTreatSet": "Кошелек или жизнь (Разбойник)",
"winter2018ConfettiSet": "Маг Конфетти", "winter2018ConfettiSet": "Конфетти (Маг)",
"winter2018GiftWrappedSet": "Подарочно-упакованный воин", "winter2018GiftWrappedSet": "Подарочно-упакованный (Воин)",
"winter2018MistletoeSet": "Омела Целитель", "winter2018MistletoeSet": "Омела (Целитель)",
"winter2018ReindeerSet": "Оленеразбойник", "winter2018ReindeerSet": "Олень (Разбойник)",
"spring2018SunriseWarriorSet": "Воин восходящего солнца (Воин)", "spring2018SunriseWarriorSet": "Утренняя заря (Воин)",
"spring2018TulipMageSet": "Тюльпанный маг (Маг)", "spring2018TulipMageSet": "Тюльпан (Маг)",
"spring2018GarnetHealerSet": "Гранатово-красный целитель (Целитель)", "spring2018GarnetHealerSet": "Гранат (Целитель)",
"spring2018DucklingRogueSet": "Гадкий утёнок (Разбойник)", "spring2018DucklingRogueSet": "Утёнок (Разбойник)",
"summer2018BettaFishWarriorSet": "Воин бойцовской рыбы (Воин)", "summer2018BettaFishWarriorSet": "Бойцовая рыбка петушок (Воин)",
"summer2018LionfishMageSet": "Магрылатка (Маг)", "summer2018LionfishMageSet": "Крылатка (Маг)",
"summer2018MerfolkMonarchSet": "Амфибия (Целитель)", "summer2018MerfolkMonarchSet": "Амфибия (Целитель)",
"summer2018FisherRogueSet": "Рыбак-разбойник (Разбойник)", "summer2018FisherRogueSet": "Рыбак-разбойник (Разбойник)",
"fall2018MinotaurWarriorSet": "Минотавр (Воин)", "fall2018MinotaurWarriorSet": "Минотавр (Воин)",

View file

@ -44,7 +44,7 @@
"hatchingPotion": "инкубационный эликсир", "hatchingPotion": "инкубационный эликсир",
"noHatchingPotions": "У вас нет инкубационных эликсиров.", "noHatchingPotions": "У вас нет инкубационных эликсиров.",
"inventoryText": "Выберите яйцо, чтобы посмотреть эликсиры, которые можно использовать с ним — они подсветятся зеленым, затем выберите один из этих эликсиров, чтобы вырастить питомца. Если ни один эликсир не будет выделен подсветкой, щелкните яйцо снова, чтобы снять выбор, а затем выберите сначала эликсир, чтобы выделить подсветкой яйца, которые можно использовать с ним. Ненужные вам предметы можно продать Торговцу Александру.", "inventoryText": "Выберите яйцо, чтобы посмотреть эликсиры, которые можно использовать с ним — они подсветятся зеленым, затем выберите один из этих эликсиров, чтобы вырастить питомца. Если ни один эликсир не будет выделен подсветкой, щелкните яйцо снова, чтобы снять выбор, а затем выберите сначала эликсир, чтобы выделить подсветкой яйца, которые можно использовать с ним. Ненужные вам предметы можно продать Торговцу Александру.",
"haveHatchablePet": "У вас имеется <%= potion %> инкубационный эликсир и <%= egg %> яйцо для выращивания этого питомца! <b>Нажмите</b> на значек лапы чтобы вырастить его.", "haveHatchablePet": "У вас имеется <%= potion %> инкубационный эликсир и <%= egg %> яйцо для выращивания этого питомца! <b>Нажмите</b> чтобы вырастить его!",
"quickInventory": "Инвентаризация запасов", "quickInventory": "Инвентаризация запасов",
"foodText": "еда", "foodText": "еда",
"food": "Корм и сёдла", "food": "Корм и сёдла",

View file

@ -513,5 +513,12 @@
"backgroundButterflyGardenText": "蝴蝶花园", "backgroundButterflyGardenText": "蝴蝶花园",
"backgroundAmongGiantFlowersNotes": "玩弄在巨花之中。", "backgroundAmongGiantFlowersNotes": "玩弄在巨花之中。",
"backgroundAmongGiantFlowersText": "在巨花之中", "backgroundAmongGiantFlowersText": "在巨花之中",
"backgrounds032020": "第70组2020年3月推出" "backgrounds032020": "第70组2020年3月推出",
"backgroundRainyBarnyardNotes": "漫步在多雨的农家庭院。",
"backgroundRainyBarnyardText": "多雨的农家庭院",
"backgroundHeatherFieldNotes": "享受石南属田的香气。",
"backgroundHeatherFieldText": "石南属田",
"backgroundAnimalCloudsNotes": "发挥你的想象力,找动物形的云朵。",
"backgroundAnimalCloudsText": "动物型的云朵",
"backgrounds042020": "第71组2020年4月推出"
} }

View file

@ -326,7 +326,7 @@
"weaponArmoireMiningPickaxNotes": "从你的任务中挖取最大量的金币!增加<%= per %>点感知。魔法衣橱采矿者套装3/3。", "weaponArmoireMiningPickaxNotes": "从你的任务中挖取最大量的金币!增加<%= per %>点感知。魔法衣橱采矿者套装3/3。",
"weaponArmoireBasicLongbowText": "基础长弓", "weaponArmoireBasicLongbowText": "基础长弓",
"weaponArmoireBasicLongbowNotes": "一把有用的传下来的弓。增加<%= str %>点力量。魔法衣橱基础射手套装1/3。", "weaponArmoireBasicLongbowNotes": "一把有用的传下来的弓。增加<%= str %>点力量。魔法衣橱基础射手套装1/3。",
"weaponArmoireHabiticanDiplomaText": "Habitica民毕业证书", "weaponArmoireHabiticanDiplomaText": "Habitica民毕业证书",
"weaponArmoireHabiticanDiplomaNotes": "重大成就的一个证书 -- 干的好!增加<%= int %>点智力。魔法衣橱毕业生套装1/3。", "weaponArmoireHabiticanDiplomaNotes": "重大成就的一个证书 -- 干的好!增加<%= int %>点智力。魔法衣橱毕业生套装1/3。",
"weaponArmoireSandySpadeText": "沙铲", "weaponArmoireSandySpadeText": "沙铲",
"weaponArmoireSandySpadeNotes": "挖掘工具,也能用沙子攻击敌方怪兽的眼睛。增加<%= str %>点力量。魔法衣橱海滨套装1/3。", "weaponArmoireSandySpadeNotes": "挖掘工具,也能用沙子攻击敌方怪兽的眼睛。增加<%= str %>点力量。魔法衣橱海滨套装1/3。",
@ -675,7 +675,7 @@
"armorMystery201604Text": "叶子护甲", "armorMystery201604Text": "叶子护甲",
"armorMystery201604Notes": "你的身周喷发着许多小但是可怕的叶子。没有属性加成。2016年4月订阅者物品。", "armorMystery201604Notes": "你的身周喷发着许多小但是可怕的叶子。没有属性加成。2016年4月订阅者物品。",
"armorMystery201605Text": "吟游诗人制服", "armorMystery201605Text": "吟游诗人制服",
"armorMystery201605Notes": "不像传统的吟游诗人参加冒险派对参加Habitic民游行乐队的吟游诗人因宏大的游行而不是因地牢突击被众所周知。没有属性加成。2016年5月订阅者物品。", "armorMystery201605Notes": "不像传统的吟游诗人参加冒险派对参加Habitica居民游行乐队的吟游诗人因宏大的游行而不是因地牢突击被众所周知。没有属性加成。2016年5月订阅者物品。",
"armorMystery201606Text": "海豹的尾巴", "armorMystery201606Text": "海豹的尾巴",
"armorMystery201606Notes": "这条有力的尾巴闪闪发光就像大海的泡沫撞碎在岸上。没有属性加成。2016年6月订阅者物品。", "armorMystery201606Notes": "这条有力的尾巴闪闪发光就像大海的泡沫撞碎在岸上。没有属性加成。2016年6月订阅者物品。",
"armorMystery201607Text": "海底盗贼护甲", "armorMystery201607Text": "海底盗贼护甲",
@ -2081,5 +2081,13 @@
"armorSpecialSpring2020MageText": "旋风长袍", "armorSpecialSpring2020MageText": "旋风长袍",
"armorSpecialSpring2020HealerText": "防护的花瓣", "armorSpecialSpring2020HealerText": "防护的花瓣",
"headAccessoryMystery202004Text": "强大的君主斑蝶触角", "headAccessoryMystery202004Text": "强大的君主斑蝶触角",
"backMystery202004Text": "强大的君主斑蝶翅膀" "backMystery202004Text": "强大的君主斑蝶翅膀",
"headAccessoryMystery202004Notes": "如果花朵的香味飘过,它们只会抽搐一下--用它们找到一个漂亮的花园没有属性加成。2020年4月订阅者物品。",
"backMystery202004Notes": "用这些美丽的翅膀快速地飞向最近绚丽的草地或在整个大陆上迁移没有属性加成。2020年4月订阅者物品。",
"shieldArmoireHobbyHorseNotes": "骑着你的俊美的爱好马迈向你的奖励!增加感知、体质各<%= attrs %>点。魔法衣橱纸骑士套装2/3。",
"shieldArmoireHobbyHorseText": "爱好马",
"armorArmoireBoxArmorNotes": "盒装甲:它适合你,所以你可以坐在... 嗯,所以你可以穿上战斗吧,像勇敢的骑士一样!增加感知、体质各<%= attrs %>点。魔法衣橱纸骑士套装3/3。 ",
"armorArmoireBoxArmorText": "盒装甲",
"weaponArmoirePaperCutterNotes": "这可能看起来并不可怕,但你有没有被纸割伤过吗?增加<%= str %>点力量。魔法衣橱纸骑士套装1/3。",
"weaponArmoirePaperCutterText": "裁纸器"
} }

View file

@ -395,7 +395,7 @@
"guildSummaryPlaceholder": "写一个简短的描述将你的公会展示给其他Habitica居民。你的公会的主要目的是什么为什么大家要加入呢尝试在概要中包含有用的关键字以便Habitica居民在搜索时可以轻松找到它", "guildSummaryPlaceholder": "写一个简短的描述将你的公会展示给其他Habitica居民。你的公会的主要目的是什么为什么大家要加入呢尝试在概要中包含有用的关键字以便Habitica居民在搜索时可以轻松找到它",
"groupDescription": "详细描述", "groupDescription": "详细描述",
"guildDescriptionPlaceholder": "在这里输入公会会员应该了解公会的所有内容。有用的提示,有帮助的链接和激励人心的标语都在这里!", "guildDescriptionPlaceholder": "在这里输入公会会员应该了解公会的所有内容。有用的提示,有帮助的链接和激励人心的标语都在这里!",
"markdownFormattingHelp": "[Markdown语法帮助](http://habitica.fandom.com/wiki/Markdown_Cheat_Sheet)", "markdownFormattingHelp": "[Markdown语法帮助](http://habitica.fandom.com/zh/wiki/Markdown格式帮助)",
"partyDescriptionPlaceholder": "这是我们队伍的描述。它描述了我们在这个队伍里做什么。如果你想了解更多关于我们在这个队伍所做的事,请阅读说明。派对继续。", "partyDescriptionPlaceholder": "这是我们队伍的描述。它描述了我们在这个队伍里做什么。如果你想了解更多关于我们在这个队伍所做的事,请阅读说明。派对继续。",
"guildGemCostInfo": "花费宝石促进高质量的公会,宝石会转移到您的公会的银行。", "guildGemCostInfo": "花费宝石促进高质量的公会,宝石会转移到您的公会的银行。",
"noGuildsTitle": "你不是任何公会的成员。", "noGuildsTitle": "你不是任何公会的成员。",

View file

@ -44,7 +44,7 @@
"hatchingPotion": "孵化药水", "hatchingPotion": "孵化药水",
"noHatchingPotions": "你没有任何孵化药水。", "noHatchingPotions": "你没有任何孵化药水。",
"inventoryText": "点选一颗蛋后可使用的药水会亮起绿色的背景然后点击药水来孵化出宠物。如果没有药水亮起绿色背景再点击一次蛋来取消点选它然后先点击药水看看可使用的宠物蛋它们同样会亮起绿色背景。你也可以在商人Alexander那里卖掉不想要的物品。", "inventoryText": "点选一颗蛋后可使用的药水会亮起绿色的背景然后点击药水来孵化出宠物。如果没有药水亮起绿色背景再点击一次蛋来取消点选它然后先点击药水看看可使用的宠物蛋它们同样会亮起绿色背景。你也可以在商人Alexander那里卖掉不想要的物品。",
"haveHatchablePet": "你有一个<%= potion %>孵化药水和<%= egg %>蛋去孵化这只宠物!<b>点击</b>爪子印孵化它。", "haveHatchablePet": "你有一个<%= potion %>孵化药水和<%= egg %>蛋去孵化这只宠物!<b>点击</b>印孵化它!",
"quickInventory": "物品栏", "quickInventory": "物品栏",
"foodText": "食物", "foodText": "食物",
"food": "宠物食品和鞍", "food": "宠物食品和鞍",

View file

@ -320,7 +320,7 @@
"questBewilderBossRageDescription": "当这个量槽被填满迷失怪就会在Habitica释放他的欺骗攻击", "questBewilderBossRageDescription": "当这个量槽被填满迷失怪就会在Habitica释放他的欺骗攻击",
"questBewilderDropBumblebeePet": "魔法蜜蜂(宠物)", "questBewilderDropBumblebeePet": "魔法蜜蜂(宠物)",
"questBewilderDropBumblebeeMount": "魔法蜜蜂(坐骑)", "questBewilderDropBumblebeeMount": "魔法蜜蜂(坐骑)",
"questBewilderBossRageMarket": "`迷失怪运用了 欺骗打击!`\n\n噢不尽管我们努力了我们迷失怪迷人的幻觉还是让我们分心忘掉了一些每日任务伴随着咯咯的叫喊声华丽的鸟儿扇动着翅膀扇起了一大团薄雾围绕着商人Alex。当雾消失他已经着魔了“有一些免费的样品”他兴高采烈地喊着开始丢出爆炸的蛋和药水朝向逃跑的Habitica民。诚然,不是最优惠的销售。\n\n快让我们保持关注我们的每日任务在它占据其他人之前打败这个怪物。", "questBewilderBossRageMarket": "`迷失怪运用了 欺骗打击!`\n\n噢不尽管我们努力了我们迷失怪迷人的幻觉还是让我们分心忘掉了一些每日任务伴随着咯咯的叫喊声华丽的鸟儿扇动着翅膀扇起了一大团薄雾围绕着商人Alex。当雾消失他已经着魔了“有一些免费的样品”他兴高采烈地喊着开始丢出爆炸的蛋和药水朝向逃跑的Habitica民。诚然,不是最优惠的销售。\n\n快让我们保持关注我们的每日任务在它占据其他人之前打败这个怪物。",
"questBewilderBossRageStables": "`迷失怪运用了 欺骗打击!` \n \n啊迷失怪再次让我们目眩神迷以至于忽视了我们的每日任务现在它已经攻击了驯兽师Matt一阵烟雾后Matt竟然变成了可怕的飞行怪兽所有的宠物和坐骑都在兽栏中痛苦地嚎叫。快专注于你的每日任务来打败这个卑鄙的怪物", "questBewilderBossRageStables": "`迷失怪运用了 欺骗打击!` \n \n啊迷失怪再次让我们目眩神迷以至于忽视了我们的每日任务现在它已经攻击了驯兽师Matt一阵烟雾后Matt竟然变成了可怕的飞行怪兽所有的宠物和坐骑都在兽栏中痛苦地嚎叫。快专注于你的每日任务来打败这个卑鄙的怪物",
"questBewilderBossRageBailey": "`迷失怪运用了 欺骗打击!`\n\n小心在报导新闻中公告员Bailey已经被迷失怪占据了她放出了一个恶魔她随意的大叫声升上了天空。现在我们该怎么办\n\n不要放弃...我们就快要击败这个讨厌的大鸟了,一劳永逸!", "questBewilderBossRageBailey": "`迷失怪运用了 欺骗打击!`\n\n小心在报导新闻中公告员Bailey已经被迷失怪占据了她放出了一个恶魔她随意的大叫声升上了天空。现在我们该怎么办\n\n不要放弃...我们就快要击败这个讨厌的大鸟了,一劳永逸!",
"questFalconText": "掠食明天之鸟", "questFalconText": "掠食明天之鸟",

View file

@ -513,5 +513,12 @@
"backgroundButterflyGardenText": "蝴蝶花園", "backgroundButterflyGardenText": "蝴蝶花園",
"backgroundAmongGiantFlowersNotes": "玩弄在巨花之中。", "backgroundAmongGiantFlowersNotes": "玩弄在巨花之中。",
"backgroundAmongGiantFlowersText": "在巨花之中", "backgroundAmongGiantFlowersText": "在巨花之中",
"backgrounds032020": "第70組2020年3月推出" "backgrounds032020": "第70組2020年3月推出",
"backgroundRainyBarnyardNotes": "漫步在多雨的农家庭院。",
"backgroundRainyBarnyardText": "多雨的农家庭院",
"backgroundHeatherFieldNotes": "享受石南屬田的香氣。",
"backgroundHeatherFieldText": "石南屬田",
"backgroundAnimalCloudsNotes": "發揮你的想像力,找動物形的雲朵。",
"backgroundAnimalCloudsText": "動物型的雲朵",
"backgrounds042020": "第71組2020年4月推出"
} }

View file

@ -2079,5 +2079,15 @@
"weaponSpecialSpring2020WarriorNotes": "在戰鬥時或逃跑時,這支翅膀會為你服務!增加<%= str %>點力量。 2020年春季限定版裝備。", "weaponSpecialSpring2020WarriorNotes": "在戰鬥時或逃跑時,這支翅膀會為你服務!增加<%= str %>點力量。 2020年春季限定版裝備。",
"weaponSpecialSpring2020WarriorText": "鋒利的翅膀", "weaponSpecialSpring2020WarriorText": "鋒利的翅膀",
"weaponSpecialSpring2020RogueNotes": "你會擊打得這麼快,任務會看起來更藍!增加<%= str %>點力量。 2020年春季限定版裝備。", "weaponSpecialSpring2020RogueNotes": "你會擊打得這麼快,任務會看起來更藍!增加<%= str %>點力量。 2020年春季限定版裝備。",
"weaponSpecialSpring2020RogueText": "青金石刀" "weaponSpecialSpring2020RogueText": "青金石刀",
"headAccessoryMystery202004Notes": "如果花朵的香味飄過,它們只會抽搐一下--用它們找到一個漂亮的花園!沒有屬性加成。 2020年4月訂閱者物品。",
"headAccessoryMystery202004Text": "強大的君主斑蝶觸角",
"backMystery202004Notes": "用這些美麗的翅膀快速地飛向最近絢麗的草地,或在整個大陸上遷移!沒有屬性加成。 2020年4月訂閱者物品。",
"backMystery202004Text": "強大的君主斑蝶翅膀",
"shieldArmoireHobbyHorseNotes": "騎著你的俊美的愛好馬邁向你的獎勵!增加感知、體質各<%= attrs %>點。魔法衣櫥紙騎士套裝2/3。",
"shieldArmoireHobbyHorseText": "愛好馬",
"armorArmoireBoxArmorNotes": "盒裝甲:它適合你,所以你可以坐在... 嗯,所以你可以穿上戰鬥吧,像勇敢的騎士一樣!增加感知、體質各<%= attrs %>點。魔法衣櫥紙騎士套裝3/3。 ",
"armorArmoireBoxArmorText": "盒裝甲",
"weaponArmoirePaperCutterNotes": "這可能看起來並不可怕,但你有沒有被紙割傷過嗎?增加<%= str %>點力量。魔法衣櫥紙騎士套裝1/3。",
"weaponArmoirePaperCutterText": "裁紙器"
} }

View file

@ -44,7 +44,7 @@
"hatchingPotion": "孵化藥水", "hatchingPotion": "孵化藥水",
"noHatchingPotions": "你沒有任何孵化藥水。", "noHatchingPotions": "你沒有任何孵化藥水。",
"inventoryText": "點選一顆蛋後可使用的藥水會亮起綠色的背景然後點擊藥水來孵化出寵物。如果沒有藥水亮起綠色背景再點擊一次蛋可以取消點選。然後先點擊藥水看看可使用的寵物蛋它們同樣會亮起綠色背景。你也可以在商人Alexander那裡賣掉不想要的物品。", "inventoryText": "點選一顆蛋後可使用的藥水會亮起綠色的背景然後點擊藥水來孵化出寵物。如果沒有藥水亮起綠色背景再點擊一次蛋可以取消點選。然後先點擊藥水看看可使用的寵物蛋它們同樣會亮起綠色背景。你也可以在商人Alexander那裡賣掉不想要的物品。",
"haveHatchablePet": "你擁有一瓶<%= potion %>孵化藥水和一顆<%= egg %>蛋,可以孵化出這隻寵物!<b>點擊</b>爪印來孵化牠。", "haveHatchablePet": "你有一個<%= potion %>孵化藥水和<%= egg %>蛋去孵化這隻寵物! <b>點擊</b>印孵化它!",
"quickInventory": "快捷背包", "quickInventory": "快捷背包",
"foodText": "食物", "foodText": "食物",
"food": "寵物食品和鞍", "food": "寵物食品和鞍",

View file

@ -247,5 +247,6 @@
"cancelYourSubscription": "取消您的訂閱?", "cancelYourSubscription": "取消您的訂閱?",
"cancelSubAlternatives": "如果您遇到技術問題或Habitica不能滿足您的要求請考慮<a href='mailto:admin@habitica.com'>與我們聯繫</a>。我們希望幫助您從Habitica獲得最大的收益。", "cancelSubAlternatives": "如果您遇到技術問題或Habitica不能滿足您的要求請考慮<a href='mailto:admin@habitica.com'>與我們聯繫</a>。我們希望幫助您從Habitica獲得最大的收益。",
"mysterySet202003": "倒刺鬥士套裝", "mysterySet202003": "倒刺鬥士套裝",
"giftASubscription": "贈送訂閱" "giftASubscription": "贈送訂閱",
"mysterySet202004": "強大的君主斑蝶套裝"
} }

View file

@ -20,6 +20,7 @@ export const CHAT_FLAG_FROM_SHADOW_MUTE = 10;
export const SUPPORTED_SOCIAL_NETWORKS = [ export const SUPPORTED_SOCIAL_NETWORKS = [
{ key: 'facebook', name: 'Facebook' }, { key: 'facebook', name: 'Facebook' },
{ key: 'google', name: 'Google' }, { key: 'google', name: 'Google' },
{ key: 'apple', name: 'Apple' },
]; ];
export const GUILDS_PER_PAGE = 30; // number of guilds to return per page when using pagination export const GUILDS_PER_PAGE = 30; // number of guilds to return per page when using pagination
@ -27,6 +28,7 @@ export const GUILDS_PER_PAGE = 30; // number of guilds to return per page when u
export const PARTY_LIMIT_MEMBERS = 30; export const PARTY_LIMIT_MEMBERS = 30;
export const MINIMUM_PASSWORD_LENGTH = 8; export const MINIMUM_PASSWORD_LENGTH = 8;
export const TRANSFORMATION_DEBUFFS_LIST = { export const TRANSFORMATION_DEBUFFS_LIST = {
snowball: 'salt', snowball: 'salt',
spookySparkles: 'opaquePotion', spookySparkles: 'opaquePotion',

View file

@ -146,6 +146,41 @@ api.loginSocial = {
}, },
}; };
// Called by apple for web authentication.
api.redirectApple = {
method: 'POST',
middlewares: [authWithHeaders({
optional: true,
})],
url: '/user/auth/apple',
async handler (req, res) {
if (req.body.id_token) {
req.body.network = 'apple';
return loginSocial(req, res);
}
let url = `/static/apple-redirect?code=${req.body.code}`;
if (req.body.user) {
const { name } = JSON.parse(req.body.user);
url += `&name=${name.firstName} ${name.lastName}`;
}
return res.redirect(303, url);
},
};
// Called as a callback by Apple. Internal route
// Can be passed `code` and `name` as query parameters
api.loginApple = {
method: 'GET',
middlewares: [authWithHeaders({
optional: true,
})],
url: '/user/auth/apple',
async handler (req, res) {
req.body.network = 'apple';
return loginSocial(req, res);
},
};
/** /**
* @api {put} /api/v3/user/auth/update-username Update username * @api {put} /api/v3/user/auth/update-username Update username
* @apiDescription Update and verify the user's username * @apiDescription Update and verify the user's username

View file

@ -0,0 +1,52 @@
import AppleAuth from 'apple-auth';
import nconf from 'nconf';
import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';
import util from 'util';
const APPLE_PRIVATE_KEY = nconf.get('APPLE_AUTH_PRIVATE_KEY');
const APPLE_AUTH_CLIENT_ID = nconf.get('APPLE_AUTH_CLIENT_ID');
const APPLE_TEAM_ID = nconf.get('APPLE_TEAM_ID');
const APPLE_AUTH_KEY_ID = nconf.get('APPLE_AUTH_KEY_ID');
const BASE_URL = nconf.get('BASE_URL');
const appleAuth = new AppleAuth(JSON.stringify({
client_id: APPLE_AUTH_CLIENT_ID, // eslint-disable-line camelcase
team_id: APPLE_TEAM_ID, // eslint-disable-line camelcase
key_id: APPLE_AUTH_KEY_ID, // eslint-disable-line camelcase
redirect_uri: `${BASE_URL}/api/v4/user/auth/apple`, // eslint-disable-line camelcase
scope: 'name email',
}), APPLE_PRIVATE_KEY, 'text');
const APPLE_PUBLIC_KEYS_URL = 'https://appleid.apple.com/auth/keys';
const appleJwksClient = jwksClient({
jwksUri: APPLE_PUBLIC_KEYS_URL,
});
const getAppleSigningKey = util.promisify(appleJwksClient.getSigningKey);
export async function appleProfile (req) {
const code = req.body.code ? req.body.code : req.query.code;
const passedToken = req.body.id_token ? req.body.id_token : req.query.id_token;
let idToken;
if (code) {
const response = await appleAuth.accessToken(code);
idToken = response.id_token;
} else if (passedToken) {
idToken = passedToken;
}
const decodedToken = jwt.decode(idToken, { complete: true });
const signingKey = await getAppleSigningKey(decodedToken.header.kid);
const applePublicKey = signingKey.getPublicKey();
const verifiedPayload = await jwt.verify(idToken, applePublicKey, { algorithms: 'RS256' });
return {
id: verifiedPayload.sub,
emails: [{ value: verifiedPayload.email }],
name: verifiedPayload.name || req.body.name || req.query.name,
};
}

View file

@ -6,6 +6,7 @@ import {
generateUsername, generateUsername,
loginRes, loginRes,
} from './utils'; } from './utils';
import { appleProfile } from './apple';
import { model as User } from '../../models/user'; import { model as User } from '../../models/user';
import { model as EmailUnsubscription } from '../../models/emailUnsubscription'; import { model as EmailUnsubscription } from '../../models/emailUnsubscription';
import { sendTxn as sendTxnEmail } from '../email'; import { sendTxn as sendTxnEmail } from '../email';
@ -24,14 +25,21 @@ function _passportProfile (network, accessToken) {
export async function loginSocial (req, res) { // eslint-disable-line import/prefer-default-export export async function loginSocial (req, res) { // eslint-disable-line import/prefer-default-export
const existingUser = res.locals.user; const existingUser = res.locals.user;
const accessToken = req.body.authResponse.access_token;
const { network } = req.body; const { network } = req.body;
const isSupportedNetwork = common.constants.SUPPORTED_SOCIAL_NETWORKS const isSupportedNetwork = common.constants.SUPPORTED_SOCIAL_NETWORKS
.find(supportedNetwork => supportedNetwork.key === network); .find(supportedNetwork => supportedNetwork.key === network);
if (!isSupportedNetwork) throw new BadRequest(res.t('unsupportedNetwork')); if (!isSupportedNetwork) throw new BadRequest(res.t('unsupportedNetwork'));
const profile = await _passportProfile(network, accessToken); let profile = {};
if (network === 'apple') {
profile = await appleProfile(req);
} else {
const accessToken = req.body.authResponse.access_token;
profile = await _passportProfile(network, accessToken);
}
if (!profile.id) throw new BadRequest(res.t('invalidData'));
let user = await User.findOne({ let user = await User.findOne({
[`auth.${network}.id`]: profile.id, [`auth.${network}.id`]: profile.id,
@ -80,7 +88,7 @@ export async function loginSocial (req, res) { // eslint-disable-line import/pre
user.newUser = true; user.newUser = true;
} }
loginRes(user, req, res); const response = loginRes(user, req, res);
// Clean previous email preferences // Clean previous email preferences
if ( if (
@ -114,5 +122,5 @@ export async function loginSocial (req, res) { // eslint-disable-line import/pre
}); });
} }
return null; return response;
} }

View file

@ -1,5 +1,6 @@
import nconf from 'nconf'; import nconf from 'nconf';
import shortid from 'short-uuid'; import shortid from 'short-uuid';
import url from 'url';
import { NotAuthorized } from '../errors'; import { NotAuthorized } from '../errors';
@ -18,6 +19,11 @@ export function loginRes (user, req, res) {
{ communityManagerEmail: COMMUNITY_MANAGER_EMAIL, userId: user._id }, { communityManagerEmail: COMMUNITY_MANAGER_EMAIL, userId: user._id },
)); ));
} }
const urlPath = url.parse(req.url).pathname;
if (req.headers['x-client'] === 'habitica-android' && urlPath.includes('apple')) {
// This is a workaround for android not being able to handle sign in with apple better.
return res.redirect(`/?id=${user._id}&key=${user.apiToken}&newUser=${user.newUser || false}`);
}
const responseData = { const responseData = {
id: user._id, id: user._id,
@ -25,6 +31,5 @@ export function loginRes (user, req, res) {
newUser: user.newUser || false, newUser: user.newUser || false,
username: user.auth.local.username, username: user.auth.local.username,
}; };
return res.respond(200, responseData); return res.respond(200, responseData);
} }

View file

@ -31,6 +31,7 @@ export default new Schema({
$type: Schema.Types.Mixed, $type: Schema.Types.Mixed,
default: () => ({}), default: () => ({}),
}, },
apple: { $type: Schema.Types.Mixed, default: () => ({}) },
local: { local: {
email: { email: {
$type: String, $type: String,