Merge branch 'develop' into release

This commit is contained in:
Sabe Jones
2020-05-14 14:03:09 -05:00
48 changed files with 594 additions and 274 deletions

49
package-lock.json generated
View File

@@ -7379,9 +7379,9 @@
}
},
"habitica-markdown": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/habitica-markdown/-/habitica-markdown-1.4.0.tgz",
"integrity": "sha512-hklG3eBILNbx/VxGeRxuk+/RiWWllcd5QLNv7Kvm2wGBRTeK9c3my2eusGuHXkwStEFGxjJD5e0iMO47cGPxYw==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/habitica-markdown/-/habitica-markdown-2.0.0.tgz",
"integrity": "sha512-70Kl/d7v1d2Rz6TFkQQ9hYcBYGAHnIPbRgS3PrW/dD/GGpN42q6gT3sCLsIpLqEXbN0EWjVscGs2qKWYLc6BMQ==",
"requires": {
"habitica-markdown-emoji": "1.2.4",
"markdown-it": "10.0.0",
@@ -9678,9 +9678,9 @@
"integrity": "sha1-EUyUlnPiqKNenTV4hSeqN7Z52is="
},
"moment": {
"version": "2.25.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.25.1.tgz",
"integrity": "sha512-nRKMf9wDS4Fkyd0C9LXh2FFXinD+iwbJ5p/lh3CHitW9kZbRbJ8hCruiadiIXZVbeAqKZzqcTvHnK3mRhFjb6w=="
"version": "2.25.3",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.25.3.tgz",
"integrity": "sha512-PuYv0PHxZvzc15Sp8ybUCoQ+xpyPWvjOuK72a5ovzp2LI32rJXOiIfyoFoYvG3s6EwwrdkMyWuRiEHSZRLJNdg=="
},
"moment-recur": {
"version": "1.0.7",
@@ -9694,6 +9694,7 @@
"version": "3.5.6",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.6.tgz",
"integrity": "sha512-sh3q3GLDLT4QmoDLamxtAECwC3RGjq+oNuK1ENV8+tnipIavss6sMYt77hpygqlMOCt0Sla5cl7H4SKCVBCGEg==",
"dev": true,
"requires": {
"bl": "^2.2.0",
"bson": "^1.1.4",
@@ -9707,6 +9708,7 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz",
"integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==",
"dev": true,
"requires": {
"readable-stream": "^2.3.5",
"safe-buffer": "^5.1.1"
@@ -9715,18 +9717,19 @@
"bson": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/bson/-/bson-1.1.4.tgz",
"integrity": "sha512-S/yKGU1syOMzO86+dGpg2qGoDL0zvzcb262G+gqEy6TgP6rt6z6qxSFX/8X6vLC91P7G7C3nLs0+bvDzmvBA3Q=="
"integrity": "sha512-S/yKGU1syOMzO86+dGpg2qGoDL0zvzcb262G+gqEy6TgP6rt6z6qxSFX/8X6vLC91P7G7C3nLs0+bvDzmvBA3Q==",
"dev": true
}
}
},
"mongoose": {
"version": "5.9.10",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.9.10.tgz",
"integrity": "sha512-w1HNukfJzzDLfcI1f79h2Wj4ogVbf+X8hRkyFgqlcjK7OnDlAgahjDMIsT+mCS9jKojrMhjSsZIs9FiRPkLqMg==",
"version": "5.9.13",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.9.13.tgz",
"integrity": "sha512-MsFdJAaCTVbDA3gYskUEpUN1kThL7sp4zh8N9rGt0+9vYMn28q92NLK90vGssM9qjOGWp8HqLeT1fBgfMZDnKA==",
"requires": {
"bson": "^1.1.4",
"kareem": "2.3.1",
"mongodb": "3.5.6",
"mongodb": "3.5.7",
"mongoose-legacy-pluralize": "1.0.2",
"mpath": "0.7.0",
"mquery": "3.2.2",
@@ -9735,6 +9738,30 @@
"safe-buffer": "5.1.2",
"sift": "7.0.1",
"sliced": "1.0.1"
},
"dependencies": {
"bl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz",
"integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==",
"requires": {
"readable-stream": "^2.3.5",
"safe-buffer": "^5.1.1"
}
},
"mongodb": {
"version": "3.5.7",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.7.tgz",
"integrity": "sha512-lMtleRT+vIgY/JhhTn1nyGwnSMmJkJELp+4ZbrjctrnBxuLbj6rmLuJFz8W2xUzUqWmqoyVxJLYuC58ZKpcTYQ==",
"requires": {
"bl": "^2.2.0",
"bson": "^1.1.4",
"denque": "^1.4.1",
"require_optional": "^1.0.1",
"safe-buffer": "^5.1.2",
"saslprep": "^1.0.0"
}
}
}
},
"mongoose-legacy-pluralize": {

View File

@@ -36,7 +36,7 @@
"gulp-imagemin": "^6.2.0",
"gulp-nodemon": "^2.5.0",
"gulp.spritesmith": "^6.9.0",
"habitica-markdown": "^1.4.0",
"habitica-markdown": "^2.0.0",
"helmet": "^3.22.0",
"image-size": "^0.8.3",
"in-app-purchase": "^1.11.3",
@@ -46,9 +46,9 @@
"lodash": "^4.17.15",
"merge-stream": "^2.0.0",
"method-override": "^3.0.0",
"moment": "^2.25.1",
"moment": "^2.25.3",
"moment-recur": "^1.0.7",
"mongoose": "^5.9.10",
"mongoose": "^5.9.13",
"morgan": "^1.10.0",
"nconf": "^0.10.0",
"node-gcm": "^1.0.2",

View File

@@ -64,6 +64,44 @@ describe('highlightMentions', () => {
expect(result[0]).to.equal(text);
});
describe('link interactions', async () => {
it('doesn\'t highlight users in link', async () => {
const text = 'http://www.medium.com/@user/blog';
const result = await highlightMentions(text);
expect(result[0]).to.equal(text);
});
it('doesn\'t highlight user in link between brackets', async () => {
const text = '(http://www.medium.com/@user/blog)';
const result = await highlightMentions(text);
expect(result[0]).to.equal(text);
});
it('doesn\'t highlight user in an autolink', async () => {
const text = '<http://www.medium.com/@user/blog>';
const result = await highlightMentions(text);
expect(result[0]).to.equal(text);
});
it('doesn\'t highlight users in link text', async () => {
const text = '[Check awesome blog written by @user](http://www.medium.com/@user/blog)';
const result = await highlightMentions(text);
expect(result[0]).to.equal(text);
});
it('doesn\'t highlight users in link with newlines and markup', async () => {
const text = '[Check `awesome` \nblog **written** by @user](http://www.medium.com/@user/blog)';
const result = await highlightMentions(text);
expect(result[0]).to.equal(text);
});
it('doesn\'t highlight users in link when followed by same @user mention', async () => {
const text = 'http://www.medium.com/@user/blog @user';
const result = await highlightMentions(text);
expect(result[0]).to.equal('http://www.medium.com/@user/blog [@user](/profile/111)');
});
});
describe('exceptions in code blocks', () => {
it('doesn\'t highlight user in inline code block', async () => {
const text = '`@user`';

View File

@@ -7265,9 +7265,9 @@
"integrity": "sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA=="
},
"bootstrap-vue": {
"version": "2.13.0",
"resolved": "https://registry.npmjs.org/bootstrap-vue/-/bootstrap-vue-2.13.0.tgz",
"integrity": "sha512-V/q2tgIY+tDxeBGOwqdPC4jk8CWmpwpSVY/1OVGxg4Jg1Jxcs6gQn7chhpI9KuBrO8diG/Nd6JRtnYwyE3b7Qw==",
"version": "2.13.1",
"resolved": "https://registry.npmjs.org/bootstrap-vue/-/bootstrap-vue-2.13.1.tgz",
"integrity": "sha512-FF1GLRvvj+TgpLkMG/A8+tCMQfWnfKpsPqt7s097VTX2lZv2/YmyhehRC1qraPHYqT7qQVy48TCxSw1H8hNh3Q==",
"requires": {
"@nuxt/opencollective": "^0.3.0",
"bootstrap": ">=4.4.1 <5.0.0",
@@ -8355,9 +8355,9 @@
"integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg=="
},
"consola": {
"version": "2.11.3",
"resolved": "https://registry.npmjs.org/consola/-/consola-2.11.3.tgz",
"integrity": "sha512-aoW0YIIAmeftGR8GSpw6CGQluNdkWMWh3yEFjH/hmynTYnMtibXszii3lxCXmk8YxJtI3FAK5aTiquA5VH68Gw=="
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/consola/-/consola-2.12.1.tgz",
"integrity": "sha512-aEkkju9ZcEa9y2MhzNhfmTUws/CEZZ0LKu0FxftSU3HygPfVMMIMSYyYct+xBN6XNRhsaDZjw2HAv3m2ammXSA=="
},
"console-browserify": {
"version": "1.2.0",
@@ -11676,9 +11676,9 @@
}
},
"habitica-markdown": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/habitica-markdown/-/habitica-markdown-1.4.0.tgz",
"integrity": "sha512-hklG3eBILNbx/VxGeRxuk+/RiWWllcd5QLNv7Kvm2wGBRTeK9c3my2eusGuHXkwStEFGxjJD5e0iMO47cGPxYw==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/habitica-markdown/-/habitica-markdown-2.0.0.tgz",
"integrity": "sha512-70Kl/d7v1d2Rz6TFkQQ9hYcBYGAHnIPbRgS3PrW/dD/GGpN42q6gT3sCLsIpLqEXbN0EWjVscGs2qKWYLc6BMQ==",
"requires": {
"habitica-markdown-emoji": "1.2.4",
"markdown-it": "10.0.0",
@@ -13068,9 +13068,9 @@
}
},
"jquery": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.0.tgz",
"integrity": "sha512-Xb7SVYMvygPxbFMpTFQiHh1J7HClEaThguL15N/Gg37Lri/qKyhRGZYzHRyLH8Stq3Aow0LsHO2O2ci86fCrNQ=="
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz",
"integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg=="
},
"js-message": {
"version": "1.0.5",
@@ -14241,9 +14241,9 @@
}
},
"moment": {
"version": "2.24.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
"version": "2.25.3",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.25.3.tgz",
"integrity": "sha512-PuYv0PHxZvzc15Sp8ybUCoQ+xpyPWvjOuK72a5ovzp2LI32rJXOiIfyoFoYvG3s6EwwrdkMyWuRiEHSZRLJNdg=="
},
"move-concurrently": {
"version": "1.0.1",

View File

@@ -29,20 +29,20 @@
"axios-progress-bar": "^1.2.0",
"babel-eslint": "^10.1.0",
"bootstrap": "^4.4.1",
"bootstrap-vue": "^2.13.0",
"bootstrap-vue": "^2.13.1",
"chai": "^4.1.2",
"core-js": "^3.6.5",
"eslint": "^6.8.0",
"eslint-config-habitrpg": "^6.2.0",
"eslint-plugin-mocha": "^5.3.0",
"eslint-plugin-vue": "^6.2.2",
"habitica-markdown": "^1.4.0",
"habitica-markdown": "^2.0.0",
"hellojs": "^1.18.4",
"inspectpack": "^4.4.0",
"intro.js": "^2.9.3",
"jquery": "^3.5.0",
"jquery": "^3.5.1",
"lodash": "^4.17.15",
"moment": "^2.24.0",
"moment": "^2.25.3",
"nconf": "^0.10.0",
"sass": "^1.26.5",
"sass-loader": "^8.0.2",

View File

@@ -18,6 +18,7 @@
<meta name="smartbanner:button-url-apple" content="https://itunes.apple.com/us/app/habitica-gamified-taskmanager/id994882113">
<meta name="smartbanner:button-url-google" content="https://play.google.com/store/apps/details?id=com.habitrpg.android.habitica">
<meta name="smartbanner:enabled-platforms" content="android,ios">
<meta name="smartbanner:hide-ttl" content="2592000000">
<link href="https://fonts.googleapis.com/css?family=Roboto+Condensed:400,400i,700,700i|Roboto:400,400i,700,700i" rel="stylesheet">
<link rel="shortcut icon" sizes="48x48" href="/static/icons/favicon.ico">
<link rel="shortcut icon" sizes="192x192" href="/static/icons/favicon_192x192.png">

View File

@@ -34,7 +34,7 @@
<div
ref="markdownContainer"
class="text"
v-html="atHighlight(parseMarkdown(msg.text))"
v-html="parseMarkdown(msg.text)"
></div>
<hr>
<div
@@ -200,9 +200,8 @@
import moment from 'moment';
import cloneDeep from 'lodash/cloneDeep';
import escapeRegExp from 'lodash/escapeRegExp';
import max from 'lodash/max';
import habiticaMarkdown from 'habitica-markdown';
import renderWithMentions from '@/libs/renderWithMentions';
import { mapState } from '@/libs/store';
import userLink from '../userLink';
@@ -211,7 +210,6 @@ import copyIcon from '@/assets/svg/copy.svg';
import likeIcon from '@/assets/svg/like.svg';
import likedIcon from '@/assets/svg/liked.svg';
import reportIcon from '@/assets/svg/report.svg';
import { highlightUsers } from '../../libs/highlightUsers';
import { CHAT_FLAG_LIMIT_FOR_HIDING, CHAT_FLAG_FROM_SHADOW_MUTE } from '@/../../common/script/constants';
export default {
@@ -245,28 +243,14 @@ export default {
...mapState({ user: 'user.data' }),
isUserMentioned () {
const message = this.msg;
if (message.highlight) return true;
const { user } = this;
if (message.highlight) return message.highlight;
message.highlight = false;
const messageText = message.text.toLowerCase();
const displayName = user.profile.name;
const username = user.auth.local && user.auth.local.username;
const mentioned = max([
messageText.indexOf(username.toLowerCase()),
messageText.indexOf(displayName.toLowerCase()),
]);
if (mentioned === -1) return message.highlight;
const escapedDisplayName = escapeRegExp(displayName);
const escapedUsername = escapeRegExp(username);
const pattern = `@(${escapedUsername}|${escapedDisplayName})(\\b)`;
const precedingChar = messageText.substring(mentioned - 1, mentioned);
if (mentioned === 0 || precedingChar.trim() === '' || precedingChar === '@') {
const regex = new RegExp(pattern, 'i');
message.highlight = regex.test(messageText);
}
const { username } = user.auth.local;
const pattern = `@(${escapeRegExp(displayName)}|${escapeRegExp(username)})(\\b)`;
message.highlight = new RegExp(pattern, 'i').test(message.text);
return message.highlight;
},
@@ -319,11 +303,7 @@ export default {
chatId: message.id,
});
if (!message.likes[this.user._id]) {
message.likes[this.user._id] = true;
} else {
message.likes[this.user._id] = !message.likes[this.user._id];
}
message.likes[this.user._id] = !message.likes[this.user._id];
this.$emit('message-liked', message);
this.$root.$emit('bv::hide::tooltip');
@@ -360,12 +340,8 @@ export default {
chatId: message.id,
});
},
atHighlight (text) {
return highlightUsers(text, this.user.auth.local.username, this.user.profile.name);
},
parseMarkdown (text) {
if (!text) return null;
return habiticaMarkdown.render(String(text));
return renderWithMentions(text, this.user);
},
},
};

View File

@@ -21,7 +21,7 @@
</p>
<div
class="text"
v-html="atHighlight(parseMarkdown(msg.text))"
v-html="parseMarkdown(msg.text)"
></div>
<div
v-if="isMessageReported"
@@ -139,13 +139,12 @@
import axios from 'axios';
import moment from 'moment';
import habiticaMarkdown from 'habitica-markdown';
import renderWithMentions from '@/libs/renderWithMentions';
import { mapState } from '@/libs/store';
import userLink from '../userLink';
import deleteIcon from '@/assets/svg/delete.svg';
import reportIcon from '@/assets/svg/report.svg';
import { highlightUsers } from '../../libs/highlightUsers';
export default {
components: {
@@ -204,12 +203,8 @@ export default {
await axios.delete(`/api/v4/inbox/messages/${message.id}`);
},
atHighlight (text) {
return highlightUsers(text, this.user.auth.local.username, this.user.profile.name);
},
parseMarkdown (text) {
if (!text) return null;
return habiticaMarkdown.render(String(text));
return renderWithMentions(text, this.user);
},
},
};

View File

@@ -170,6 +170,7 @@ const NOTIFICATIONS = {
achievement: true,
label: $t => $t('modalContribAchievement'),
modalId: 'contributor',
sticky: true,
},
ACHIEVEMENT_ALL_YOUR_BASE: {
achievement: true,
@@ -552,7 +553,7 @@ export default {
this.text(config.label(this.$t), () => {
this.notificationData = data;
this.$root.$emit('bv::show::modal', config.modalId);
}, true, 10000);
}, !config.sticky, 10000);
}
},
debounceCheckUserAchievements: debounce(function debounceCheck () {

View File

@@ -1,26 +0,0 @@
import escapeRegExp from 'lodash/escapeRegExp';
const optionalAnchorTagRegExStr = '(<\\w[^>]*)?'; // everything including the anchor tag is recognized
const mentionRegExStr = '(@(?:<(?:em|strong)>)?[\\w-]+(?:<\\/(?:em|strong)>)?)';
const optionalPostMentionRegExStr = '(\\.\\w+)?'; // like dot-TLD
const finalMentionRegEx = new RegExp(`${optionalAnchorTagRegExStr}${mentionRegExStr}${optionalPostMentionRegExStr}`, 'gi');
export function highlightUsers (text, userName, displayName) { // eslint-disable-line import/prefer-default-export, max-len
const currentUser = [`@${userName}`, `@${displayName}`].map(escapeRegExp);
text = text.replace(finalMentionRegEx, (fullMatched, preMention, mentionStr, postMention) => { // eslint-disable-line no-param-reassign, max-len
if ((preMention && preMention.includes('<a')) || Boolean(postMention)) {
return fullMatched;
}
let fixedStr = mentionStr.replace(/<\/?em>/g, '_');
fixedStr = fixedStr.replace(/<\/?strong>/g, '__');
const isUserMention = currentUser.includes(fixedStr) ? 'at-highlight' : '';
return fullMatched.replace(mentionStr, `<span class="at-text ${isUserMention}">${fixedStr}</span>`);
});
return text;
}

View File

@@ -0,0 +1,7 @@
import habiticaMarkdown from 'habitica-markdown/withMentions';
export default function renderWithMentions (text, user) {
if (!text) return null;
const env = { userName: user.auth.local.username, displayName: user.profile.name };
return habiticaMarkdown.render(String(text), env);
}

View File

@@ -0,0 +1,58 @@
import { shallowMount, createLocalVue } from '@vue/test-utils';
import ChatCard from '@/components/chat/chatCard.vue';
import Store from '@/libs/store';
const localVue = createLocalVue();
localVue.use(Store);
describe('ChatCard', () => {
function createMessage (text) {
return { text, likes: {} };
}
function createUser (username) {
return {
auth: { local: { username } },
profile: { name: username },
contributor: {},
flags: {},
};
}
const message = createMessage('test');
let wrapper;
beforeEach(() => {
wrapper = shallowMount(ChatCard, {
propsData: { msg: message },
store: new Store({
state: {
user: { data: createUser('Tester') },
},
getters: {},
actions: {},
}),
localVue,
mocks: { $t: string => string },
});
});
it('shows the message text', () => {
expect(wrapper.find('div.text').text()).to.equal(message.text);
expect(wrapper.find('div.mentioned-icon').exists()).to.be.false;
});
it('shows mention dot if user is mentioned', () => {
wrapper.setProps({ msg: createMessage('@Tester') });
expect(wrapper.find('div.mentioned-icon').exists()).to.be.true;
});
// Bug fixed by https://github.com/HabitRPG/habitica/pull/12177
it('shows mention dot if user is mentioned after almostmention', () => {
wrapper.setProps({ msg: createMessage('thetester @Tester') });
expect(wrapper.find('div.mentioned-icon').exists()).to.be.true;
});
});

View File

@@ -1,11 +1,20 @@
import habiticaMarkdown from 'habitica-markdown';
import { highlightUsers } from '@/libs/highlightUsers';
import renderMarkdown from '@/libs/renderWithMentions';
describe('renderWithMentions', () => {
function user (name, displayName) {
return { auth: { local: { username: name } }, profile: { name: displayName } };
}
it('returns null if no text supplied', () => {
const result = renderMarkdown('', user('a', 'b'));
expect(result).to.be.null;
});
describe('highlightUserAndEmail', () => {
it('highlights displayname', () => {
const text = 'hello @displayedUser with text after';
const result = highlightUsers(text, 'user', 'displayedUser');
const result = renderMarkdown(text, user('user', 'displayedUser'));
expect(result).to.contain('<span class="at-text at-highlight">@displayedUser</span>');
});
@@ -13,45 +22,42 @@ describe('highlightUserAndEmail', () => {
it('highlights username', () => {
const text = 'hello @user';
const result = highlightUsers(text, 'user', 'displayedUser');
const result = renderMarkdown(text, user('user', 'displayedUser'));
expect(result).to.contain('<span class="at-text at-highlight">@user</span>');
});
it('highlights username sandwiched with underscores', () => {
const text = 'hello @<em>user</em>';
const text = 'hello @_user_';
const result = highlightUsers(text, '_user_', 'displayedUser');
const result = renderMarkdown(text, user('_user_', 'displayedUser'));
expect(result).to.contain('<span class="at-text at-highlight">@_user_</span>');
expect(result).to.not.contain('<em>');
expect(result).to.not.contain('</em>');
});
it('highlights username sandwiched with double underscores', () => {
const text = 'hello @<strong>user</strong>';
const text = 'hello @__user__';
const result = highlightUsers(text, 'diffUser', 'displayDiffUser');
expect(result).to.contain('<span class="at-text ">@__user__</span>');
const result = renderMarkdown(text, user('diffUser', 'displayDiffUser'));
expect(result).to.contain('<span class="at-text">@__user__</span>');
expect(result).to.not.contain('<strong>');
expect(result).to.not.contain('</strong>');
});
it('not highlights any email', () => {
const text = habiticaMarkdown.render('hello@example.com');
const result = renderMarkdown('hello@example.com', user('example', 'displayedUser'));
const result = highlightUsers(text, 'example', 'displayedUser');
expect(result).to.not.contain('<span class="at-highlight">@example</span>');
});
it('complex highlight', () => {
const plainText = 'a bit more @mentions to @use my@mentions.com broken.@mail.com';
const plainText = 'a bit more @mentions to @use my@mentions.com broken @mail.com';
const text = habiticaMarkdown.render(plainText);
const result = highlightUsers(text, 'use', 'mentions');
const result = renderMarkdown(plainText, user('use', 'mentions'));
expect(result).to.contain('<span class="at-text at-highlight">@mentions</span>');
expect(result).to.contain('<span class="at-text at-highlight">@use</span>');
expect(result).to.contain('<span class="at-text">@mail</span>');
expect(result).to.not.contain('<span class="at-text at-highlight">@mentions</span>.com');
});
});

View File

@@ -5,7 +5,7 @@
"levelup": "Изпълнявайки целите си в истинския живот, Вие качихте ниво и здравето Ви беше запълнено!",
"reachedLevel": "Достигнахте ниво <%= level %>",
"achievementLostMasterclasser": "Изпълнител на Мисии: Серия на Класовите Повелители",
"achievementLostMasterclasserText": "Завършихте шестнадесет мисии от Серията на Класовите Повелители и разрешихте загадката на Изгубената Класова Повелителка!",
"achievementLostMasterclasserText": "Завършихте шестнадесетте мисии от Серията на Класовите Повелители и разрешихте загадката на Изгубената Класова Повелителка!",
"achievementUndeadUndertaker": "Нежив Погребален Директор",
"achievementUndeadUndertakerModalText": "Опитомихте всички Зомби Оседлани Зверове!",
"achievementUndeadUndertakerText": "Опитомили са всички Зомби Оседлани Зверове.",
@@ -13,9 +13,9 @@
"achievementMonsterMagusText": "Събрали са всички Зомби Любимци.",
"achievementMonsterMagus": "Магьосник на Зверове",
"achievementPartyUp": "Присъеденихте се с член на дружината Ви!",
"achievementPartyOn": "Дружината ти порасна до 4 члена!",
"achievementKickstarter2019Text": "Подкрепи 2019ят Проект за Значки",
"achievementKickstarter2019": "Значка за Кикстартър Подкрепа",
"achievementPartyOn": "Дружината ви порасна до 4 члена!",
"achievementKickstarter2019Text": "Подкрепили са 2019ят Kickstarter Проект за Значки",
"achievementKickstarter2019": "Значка за Kickstarter Подкрепа",
"achievementAridAuthorityModalText": "Опитомихте всички Пустинни Оседлани Зверове!",
"achievementAridAuthorityText": "Опитомили са всички Пустинни Оседлани Зверове.",
"achievementAridAuthority": "Безплодният Господар",
@@ -23,16 +23,60 @@
"achievementDustDevilText": "Събрали са всички Пустинни Любимци.",
"achievementDustDevil": "Прашен Дявол",
"achievementBackToBasicsText": "Събрали са всички Основни Любимци.",
"achievementBackToBasicsModalText": "Събрали сте всички Основни Любимци!",
"achievementBackToBasicsModalText": "Събрахте сте всички Основни Любимци!",
"achievementAllYourBaseModalText": "Опитомихте всички Основни Оседлани Зверове!",
"achievementAllYourBaseText": "Опитомили са всички Основни Оседлани Зверове.",
"achievementAllYourBase": "Всичките Ви Бази",
"achievementMindOverMatterModalText": "Завършихте мисиите за Каменни, Слузести, и Преждови любимци!",
"achievementBackToBasics": "Отново на Основите",
"achievementJustAddWaterModalText": "Вие завършихте мисиите за Октопод, Водно Конче, Кит, Костенурка, Воден Плужек, Воден Змей, и Делфин любимец!",
"achievementJustAddWaterText": "Забършихте миисите за Октопод, Водно Конче, Сепия, Кит, Костенурка, Воден Плужек, Воден Змей, и Делфин любимци.",
"achievementJustAddWaterText": "Завършили са мисиите за Октопод, Водно Конче, Сепия, Кит, Костенурка, Воден Плужек, Воден Змей, и Делфин любимци.",
"achievementJustAddWater": "Само Добави Вода",
"achievementMindOverMatterText": "Завършили са мисиите за Каменни, Слузести, и Преждови любимци.",
"achievementMindOverMatter": "Умът Преди Физическия Свят",
"achievementLostMasterclasserModalText": "Вие приключихте всички шестнадесет мисии в Серията Класовите Повелители, и разреши мистерията на Загубеният Клас Повелител!"
"achievementLostMasterclasserModalText": "Вие приключихте всички шестнадесет мисии в Серията Класовите Повелители, и разрешихте мистерията на Загубеният Клас Повелител!",
"achievementBugBonanzaModalText": "Завършихте мисиите за Бръмбар, Пеперуда, Охлюв и Паяк!",
"achievementBugBonanzaText": "Завършили са всички мисии за Бръмбар, Пеперуда, Охлюв и Паяк.",
"achievementBugBonanza": "Богат с Насекоми",
"achievementRosyOutlookModalText": "Опитомихте всички Памучна Захар Розови Оседлани Зверове!",
"achievementRosyOutlookText": "Опитомили са всички Памучна Захар Розови Оседлани Зверове.",
"achievementRosyOutlook": "Розов Изглед",
"achievementTickledPinkModalText": "Събрахте всички Памучна Захар Розови Животни!",
"achievementTickledPinkText": "Събрали са всички Памучна Захар Розови Животни.",
"achievementTickledPink": "Розов Гъдел",
"achievementPrimedForPaintingText": "Събраха всички Бели Животни.",
"achievementPurchasedEquipmentModalText": "Екипировката е начин да промените аватара си и да подобрите Показателите си",
"achievementPurchasedEquipmentText": "Поръчаха първото си парче екипировка.",
"achievementPurchasedEquipment": "Купете част от Екипировка",
"achievementFedPetText": "Нахраниха първото си животно.",
"achievementHatchedPetText": "Излюпиха първото си животно.",
"achievementCompletedTaskModalText": "Отбележете задачите си и бъдете възнаградени",
"achievementCompletedTaskText": "Завършиха първата си задача.",
"achievementCompletedTask": "Завършете задача",
"achievementCreatedTaskModalText": "Добави задача за нещо което искате да постигнете тази седмица",
"achievementCreatedTask": "Създайте първата си задача",
"achievementCreatedTaskText": "Създадоха са първата си задача.",
"foundNewItemsCTA": "Отидете в Инвентар и смесете новата ви излюпваща отвара с яйце!",
"foundNewItemsExplanation": "Завършека на задачи ви дава шанс да намерите предмети като Яйца, Излюпващи Отвари и Животинска Храна.",
"foundNewItems": "Намерихте нови предмети!",
"hideAchievements": "Скрий <%= category %>",
"showAllAchievements": "Покажи Всички",
"onboardingCompleteDescSmall": "Ако желаете още повече, отидете в Постижения и започнете събирането!",
"onboardingCompleteDesc": "Получихте <strong>5 Постижения</strong> и <strong class=\"gold-amount\">100 Злато</strong> за завършения списък.",
"onboardingComplete": "Завършихте задачите си!",
"earnedAchievement": "Получихте постижение!",
"yourProgress": "Вашият Напредък",
"gettingStartedDesc": "Изпълнете задачите и ще получите <strong>5 Постижения </strong> и <strong class=\"gold-amount\">100 Злато </strong>при завършване!",
"achievementPrimedForPaintingModalText": "Събрахте всички Бели Животни!",
"achievementPearlyProModalText": "Опитомихте всички Бели Оседлани Зверове!",
"achievementPearlyProText": "Опетомили са всички Оседлани Зверове.",
"achievementPearlyPro": "Перлен Майстор",
"achievementPrimedForPainting": "Готов за Рисуване",
"achievementFedPetModalText": "Има различни видове храна, но Животните могат да са капризни",
"achievementFedPet": "Нахрани Животно",
"achievementHatchedPetModalText": "Отиди в инвентара си и смеси излюпваща отвара с Яйце",
"achievementHatchedPet": "Излюпи Животно",
"viewAchievements": "Постижения",
"letsGetStarted": "Нека да започнем!",
"onboardingProgress": "<%= percentage %>% напредък"
}

View File

@@ -24,5 +24,42 @@
"defaultTag4": "Училище",
"defaultTag5": "Екипи",
"defaultTag6": "Домакинска работа",
"defaultTag7": "Изобретателност"
"defaultTag7": "Изобретателност",
"defaultHabitNotes": "Или го изтрийте от екрана за редакция",
"defaultHabitText": "Кликнете тук за да заместите това с вреден навик",
"creativityTodoNotes": "Натиснете за да кръстите проекта",
"creativityTodoText": "Завърши креативен проект",
"creativityDailyNotes": "Натиснете за да кръстите текущия проект и да посочите срок!",
"creativityDailyText": "Работи върху креативен проект",
"creativityHabit": "Изучи занаят >> + Практикувах нова креативна техника",
"choresTodoNotes": "Натиснете за да посочите разхвърляното място!",
"choresTodoText": "Подреди гардероб >> Подреди неразбория",
"choresDailyNotes": "Натиснете за да изберете графика си!",
"choresDailyText": "Измий съдовете",
"choresHabit": "10 минути чистене",
"selfCareTodoNotes": "Натиснете за да посочите планирани действия!",
"selfCareTodoText": "Извърши забавно занимание",
"selfCareDailyNotes": "Натиснете за да изберете графика си!",
"selfCareDailyText": "5 минути тихо дишане",
"selfCareHabit": "Направи кратка почивка",
"schoolTodoNotes": "Натиснете за да кръстите заданието и да посочите срока!",
"schoolTodoText": "Завърши училищно задание",
"schoolDailyNotes": "Натиснете за да посочите графика ви за домашни!",
"schoolDailyText": "Завърши домашно",
"schoolHabit": "Учи/Отложи го",
"healthTodoNotes": "Натиснете за да добавите списъци!",
"healthTodoText": "Преглед на графика >> Измисли полезна промяна",
"healthDailyNotes": "Натиснете за промени!",
"healthDailyText": "Почисти си зъбите с нишка",
"healthHabit": "Яж Полезна/Вредна Храна",
"exerciseTodoNotes": "Натиснете за да добавите списък!",
"exerciseTodoText": "Настройте тренировъчен график",
"exerciseDailyNotes": "Натиснете за да посочите вашият график и упражнения!",
"exerciseDailyText": "Разтягане >> Дневна тренировъчна рутина",
"exerciseHabit": "10 мин. кардио >> + 10 минути кардио",
"workTodoProjectNotes": "Натиснете за да определите името на текущия проект + срок за завършека му!",
"workTodoProject": "Работен проект >> Завърши работен проект",
"workDailyImportantTaskNotes": "Натиснете за да определите най-важната задача",
"workDailyImportantTask": "Най-важна задача >> Работех върху най-важната дневна задача",
"workHabitMail": "Провери пощата си"
}

View File

@@ -10,7 +10,7 @@
"gearNotOwned": "Не притежавате този предмет.",
"noGearItemsOfType": "Не притежавате нищо от тези.",
"noGearItemsOfClass": "Вече имате всичката възможна класова екипировка! Още ще стане налична по време на големите празненства, около слънцестоенията и равноденствията.",
"classLockedItem": "Този предмет е достъпен само за определен клас. Променете класа си от Потребителската иконка > Настройки > Изграждане на героя!",
"classLockedItem": "Този предмет е достъпен само за определен клас. От ниво 10 и нагоре, можете да промените класа си от Потребителската иконка > Настройки > Изграждане на героя!",
"tierLockedItem": "Този предмет е достъпен само след като вече сте закупили предходните предмети в последователността. Продължавайте да работите!",
"sortByType": "Тип",
"sortByPrice": "Цена",
@@ -279,7 +279,7 @@
"weaponSpecialWinter2019WarriorText": "Снежинкова алебарда",
"weaponSpecialWinter2019WarriorNotes": "Тази снежинка е изкуствено изработена кристалче по кристалче, за да се превърне в здраво като диамант острие! Увеличава силата с <%= str %>. Ограничена серия: Зимна екипировка 2018-2019 г.",
"weaponSpecialWinter2019MageText": "Скиптър на огнен дракон",
"weaponSpecialWinter2019MageNotes": "Внимавайте! Този взривоопасен скиптър е готов да Ви помогне да се справите с всички нападатели. Увеличава интелигентността с <%= int %> и усета с <%= per %>. Ограничена серия: Зимна екипировка 2018-2019",
"weaponSpecialWinter2019MageNotes": "Внимавайте! Този взривоопасен скиптър е готов да Ви помогне да се справите с всички нападатели. Увеличава интелигентността с <%= int %> и усета с <%= per %>. Ограничена серия: Зимна екипировка 2018-2019.",
"weaponSpecialWinter2019HealerText": "Зимна вълшебна пръчка",
"weaponSpecialWinter2019HealerNotes": "Зимата може да бъде време за почивка и възстановяване, така че тази зимна вълшебна пръчка може да Ви помогне и срещу най-тежките болежки. Увеличава интелигентността с <%= int %>. Ограничена серия: Зимна екипировка 2018-2019 г.",
"weaponMystery201411Text": "Вилица на изобилието",
@@ -1747,5 +1747,12 @@
"eyewearArmoirePlagueDoctorMaskNotes": "Съвсем истинска маска, носена от лекарите, които са се борили срещу чумата на протакането. Увеличава якостта и интелигентността с по <%= attrs %>. Омагьосан гардероб: комплект „Чумен лекар“ (предмет 2 от 3).",
"eyewearArmoireGoofyGlassesText": "Глупави очила",
"eyewearArmoireGoofyGlassesNotes": "Идеална дегизировка или нещо, на което да се посмеят приятелите Ви на следващия купон. Увеличава усета с <%= per %>. Омагьосан гардероб: Независим предмет.",
"twoHandedItem": "Предмет за две ръце."
"twoHandedItem": "Предмет за две ръце.",
"weaponSpecialSpring2019MageText": "Кехлибарен Жезъл",
"weaponSpecialSpring2019WarriorNotes": "Лошите навици треперят под това острие. Подобрява Сила със <%= str %>. Лимитирана Серия 2019, Пролетна Екипировка.",
"weaponSpecialSpring2019WarriorText": "Стволов Меч",
"weaponSpecialSpring2019RogueNotes": "Тези оръжия съдържат мощта на небето и дъжда. Не препоръчваме да се използва под вода. Увеличава Сила със <%= str %>. Ограничена Серия 2019, Пролетна Екипировка.",
"weaponSpecialSpring2019RogueText": "Светкавица",
"weaponSpecialKS2019Notes": "Огънат като човката и ноктите на грифон, този богато украсен полюс ти напомня да проявиш упоритост когато задачата изглежда невъзможна. Увеличава Сила със <%= str %>.",
"weaponSpecialKS2019Text": "Митичен Грифон Глайв"
}

View File

@@ -85,45 +85,45 @@
"scarecrowWarriorSet": "Плашило (воин)",
"stitchWitchSet": "Шиеща вещица (магьосник)",
"potionerSet": "Отвараджия (лечител)",
"battleRogueSet": "Боен мошеник (мошеник)",
"battleRogueSet": "Боен (мошеник)",
"springingBunnySet": "Подскачащо зайче (лечител)",
"grandMalkinSet": "Големия Малкин (магьосник)",
"cleverDogSet": "Умно куче (мошеник)",
"braveMouseSet": "Смела мишка (воин)",
"summer2016SharkWarriorSet": "Акула-воин (воин)",
"summer2016DolphinMageSet": "Делфин-магьосник (магьосник)",
"summer2016SeahorseHealerSet": "Морско конче-лечител (лечител)",
"summer2016EelSet": "Змиорка-мошеник (мошеник)",
"summer2016SharkWarriorSet": "Акула (воин)",
"summer2016DolphinMageSet": "Делфин (магьосник)",
"summer2016SeahorseHealerSet": "Морско конче (лечител)",
"summer2016EelSet": "Змиорка (мошеник)",
"fall2016SwampThingSet": "Блатно същество (воин)",
"fall2016WickedSorcererSet": "Шантав магьосник (магьосник)",
"fall2016GorgonHealerSet": "Горгонски лечител (лечител)",
"fall2016BlackWidowSet": "Мошеник-черна вдовица (мошеник)",
"fall2016GorgonHealerSet": "Горгон (лечител)",
"fall2016BlackWidowSet": "Черна Вдовица (мошеник)",
"winter2017IceHockeySet": "Леден хокеист (воин)",
"winter2017WinterWolfSet": "Зимен вълк (магьосник)",
"winter2017SugarPlumSet": "Захарен лечител (лечител)",
"winter2017FrostyRogueSet": "Леден мошеник (мошеник)",
"spring2017FelineWarriorSet": "Котешки воин (воин)",
"winter2017SugarPlumSet": "Захарна Слива (лечител)",
"winter2017FrostyRogueSet": "Леден (мошеник)",
"spring2017FelineWarriorSet": "Котешки (воин)",
"spring2017CanineConjurorSet": "Кучешки вълшебник (магьосник)",
"spring2017FloralMouseSet": "Цветна мишка (лечител)",
"spring2017SneakyBunnySet": "Промъкващо се зайче (мошеник)",
"summer2017SandcastleWarriorSet": "Пясъчно-замъков воин (воин)",
"summer2017WhirlpoolMageSet": "Водовъртежен магьосник (магьосник)",
"summer2017SandcastleWarriorSet": "Пясъчен-замъков (воин)",
"summer2017WhirlpoolMageSet": "Водовъртежен (магьосник)",
"summer2017SeashellSeahealerSet": "Миден лечител (лечител)",
"summer2017SeaDragonSet": "Морски дракон (мошеник)",
"fall2017HabitoweenSet": "Хабитоуински воин (воин)",
"fall2017MasqueradeSet": "Маскараден магьосник (магьосник)",
"fall2017HauntedHouseSet": "Прокълнат къщен лечител (лечител)",
"fall2017TrickOrTreatSet": "Мошеник на лакомствата и пакостите (мошеник)",
"winter2018ConfettiSet": "Конфетен магьосник (магьосник)",
"winter2018GiftWrappedSet": "Опакован като подарък воин (воин)",
"winter2018MistletoeSet": "Имелен лечител (лечител)",
"winter2018ReindeerSet": "Еленов мошеник (мошеник)",
"spring2018SunriseWarriorSet": "Воин на зората (воин)",
"spring2018TulipMageSet": "Магьосник на лалето (магьосник)",
"spring2018GarnetHealerSet": "Гранатен лечител (лечител)",
"spring2018DucklingRogueSet": "Патешки мошеник (мошеник)",
"summer2018BettaFishWarriorSet": "Сиамски рибен воин (воин)",
"summer2018LionfishMageSet": "Лъвски рибен магьосник (магьосник)",
"fall2017HabitoweenSet": "Хабитоуински (воин)",
"fall2017MasqueradeSet": "Маскараден (магьосник)",
"fall2017HauntedHouseSet": "Прокълната Къща (лечител)",
"fall2017TrickOrTreatSet": "Лакомство или Пакост (мошеник)",
"winter2018ConfettiSet": "Конфетен (магьосник)",
"winter2018GiftWrappedSet": "Подаръчно опакован (воин)",
"winter2018MistletoeSet": "Имелен (лечител)",
"winter2018ReindeerSet": "Елен (мошеник)",
"spring2018SunriseWarriorSet": "Зора (воин)",
"spring2018TulipMageSet": "Лале (магьосник)",
"spring2018GarnetHealerSet": "Гранат (лечител)",
"spring2018DucklingRogueSet": "Патенце (мошеник)",
"summer2018BettaFishWarriorSet": "Сиамска Бойна Риба (воин)",
"summer2018LionfishMageSet": "Риба-Лъв (магьосник)",
"summer2018MerfolkMonarchSet": "Цар на русалките (лечител)",
"summer2018FisherRogueSet": "Рибар-мошеник (мошеник)",
"fall2018MinotaurWarriorSet": "Минотавър (воин)",
@@ -143,13 +143,40 @@
"dateEndAugust": "31 август",
"dateEndSeptember": "21 септември",
"dateEndOctober": "31 октомври",
"dateEndNovember": "3 декември",
"dateEndNovember": "30 Ноември",
"dateEndJanuary": "31 януари",
"dateEndFebruary": "28 февруари",
"dateEndFebruary": "29 февруари",
"winterPromoGiftHeader": "ПОДАРЕТЕ АБОНАМЕНТ И ЩЕ ПОЛУЧИТЕ ОЩЕ ЕДИН БЕЗПЛАТНО!",
"winterPromoGiftDetails1": "Само до 15 януари, ако подарите абонамент на някого, ще получите същия и за себе си безплатно!",
"winterPromoGiftDetails1": "Само до 6 януари, ако подарите абонамент на някого, ще получите същия и за себе си безплатно!",
"winterPromoGiftDetails2": "Моля, имайте предвид, че ако Вие или получателят на подаръка Ви вече имате повтарящ се абонамент, подареният ще започне след като текущият бъде прекратен или изтече. Благодарим Ви за подкрепата! <3",
"discountBundle": "пакет",
"g1g1Announcement": "Подарете абонамент и ще получите още един безплатно! Събитието протича в момента!",
"g1g1Details": "Подарете абонамент на приятел от профила му и ще получите същия абонамент безплатно!"
"g1g1Details": "Подарете абонамент на приятел от профила му и ще получите същия абонамент безплатно!",
"september2018": "Септември 2018",
"september2017": "Септември 2017",
"marchYYYY": "Март <%= year %>",
"decemberYYYY": "Декември <%= year %>",
"augustYYYY": "Август <%= year %>",
"eventAvailabilityReturning": "Налична за покупка до<%= availableDate(locale) %>. Отварата беше налична до<%= previousDate(locale) %>.",
"spring2020LapisLazuliRogueSet": "Лапис Лазули (мошеник)",
"spring2020IrisHealerSet": "Ирис (лечител)",
"spring2020PuddleMageSet": "Локва (магьосник)",
"spring2020BeetleWarriorSet": "Носорог Бръмбар (воин)",
"winter2020LanternSet": "Фенер (мошеник)",
"winter2020WinterSpiceSet": "Люта Зима (лечител)",
"winter2020CarolOfTheMageSet": "Песента на Магьосника (магьосник)",
"winter2020EvergreenSet": "Вечнозелен (воин)",
"fall2019RavenSet": "Гарван (воин)",
"fall2019LichSet": "Лич (лечител)",
"fall2019CyclopsSet": "Циклоп (магьосник)",
"fall2019OperaticSpecterSet": "Оперативен Призрак",
"summer2019HammerheadRogueSet": "Риба-Чук (мошеник)",
"summer2019ConchHealerSet": "Раковина (лечител)",
"summer2019WaterLilyMageSet": "Водна Лилия (магьосник)",
"summer2019SeaTurtleWarriorSet": "Костенурка (воин)",
"spring2019CloudRogueSet": "Облак (мошеник)",
"spring2019RobinHealerSet": "Червеношийка (лечител)",
"spring2019AmberMageSet": "Кехлибар (Магьосник)",
"spring2019OrchidWarriorSet": "Орхидея (воин)",
"june2018": "Юни 2018"
}

View File

@@ -1,11 +1,11 @@
{
"questEvilSantaText": "Ловджията Дядо Коледа",
"questEvilSantaNotes": "Чуваш приглушен рев, идващ някъде дълбоко от ледените полета. Следваш звука му, смесен с някакъв странен кикот, до едно голо място в гората, където откриваш голяма полярна мечка. Тя е окована, натъпкана в клетка и се бори за живота си. Върху клетката танцува малко злобно дяволче, облечено в някакви дрипи. Победи ловджията Дядо Коледа и спаси мечката!",
"questEvilSantaNotes": "Чуваш приглушен рев, идващ някъде дълбоко от ледените полета. Следваш звука му, смесен с някакъв странен кикот, до едно голо място в гората, където откриваш голяма полярна мечка. Тя е окована, натъпкана в клетка и се бори за живота си. Върху клетката танцува малко злобно дяволче, облечено в някакви дрипи. Победи ловджията Дядо Коледа и спаси мечката!<br><br><strong>Забележка</strong>: “ловджията Дядо Коледа” възнаграждава натрупващо се постижение от мисия, но дава рядък Оседлан Звяр който може да бъде добавен в конюшнята само веднъж.",
"questEvilSantaCompletion": "Ловджията Дядо Коледа изревава от яд и изчезва в нощта. Благодарната мечка се опитва да ти каже нещо с рев и ръмжене. Отвеждаш я в конюшнята, където Мат Бош, господарят на зверовете, изслушва разказа ѝ със затаен дъх. Тя има малко мече! То е избягало в ледените полета когато мама мечка била заловена.",
"questEvilSantaBoss": "Ловджията Дядо Коледа",
"questEvilSantaDropBearCubPolarMount": "Полярна мечка (превоз)",
"questEvilSanta2Text": "Търсенето на мечето",
"questEvilSanta2Notes": "Когато ловджията Дядо Коледа хванал полярната мечка, малкото ѝ мече избягало в ледените полета. Чуваш звуците на чупещи се клончета и хрущящ сняг, в тишината на гората. Следи от лапи! Започваш да ги следваш тичешком. Открий всички следи и счупени клонки и върни мечето!",
"questEvilSanta2Notes": "Когато ловджията Дядо Коледа хванал полярната мечка, малкото ѝ мече избягало в ледените полета. Чуваш звуците на чупещи се клончета и хрущящ сняг, в тишината на гората. Следи от лапи! Започваш да ги следваш тичешком. Открий всички следи и счупени клонки и върни мечето!<br><br><strong>Забележка</strong>: “Намери Мечето” възнаграждава натрупващо се постижение от мисия, но дава рядък Оседлан Звяр който може да бъде добавен в конюшнята само веднъж.",
"questEvilSanta2Completion": "Открихте мечето! То ще бъде с Вас завинаги.",
"questEvilSanta2CollectTracks": "Следи",
"questEvilSanta2CollectBranches": "Счупени клонки",
@@ -15,49 +15,49 @@
"questGryphonCompletion": "Победен, могъщият звяр засрамено пропълзява обратно в бърлогата на своя господар. „Браво, приключенци!“ — възкликва <strong>baconsaur</strong> — „Ето, вземете няколко от яйцата на грифона. Сигурен съм, че ще успеете да ги отгледате добре!“",
"questGryphonBoss": "Огнен грифон",
"questGryphonDropGryphonEgg": "Грифон (яйце)",
"questGryphonUnlockText": "Отключва възможността за купуване на яйца на грифон яйца от пазара.",
"questGryphonUnlockText": "Отключва възможността за купуване на Яйца на грифон от Пазара",
"questHedgehogText": "Ежко-Звережко",
"questHedgehogNotes": "Таралежите са странни животни. Те са едни от най-преданите домашни любимци, които Хабитанците могат да имат. Носи се слух, че ако ги храниш с мляко след полунощ, стават доста раздразнителни. И петдесет пъти по-големи. Изглежда <strong>InspectorCaracal</strong> направи точно това. Опа.",
"questHedgehogCompletion": "Групата ви успя да успокои таралежа! След като се смалява до нормалния си размер, тя се дотътря до яйцата си. Връща се скимтейки и побутва няколко от яйцата си към групата ви. Дано тези таралежи понасят млякото по-добре!",
"questHedgehogBoss": "Ежко-Звережко",
"questHedgehogDropHedgehogEgg": "Таралеж (яйце)",
"questHedgehogUnlockText": "Отключва възможността за купуване на яйца на таралеж от пазара.",
"questHedgehogUnlockText": "Отключва възможността за купуване на Яйца на таралеж от Пазара",
"questGhostStagText": "Пролетен дух",
"questGhostStagNotes": "Ех, пролет! Онова време от годината, когато цветовете отново започват да изпълват пейзажа. Няма ги вече студените и заснежени зимни хълмове. Там, където преди е имало сняг, сега има оживена растителност. Зелени листа изпълват дърветата, тревата си връща предишния ярък цвят, дъга от цветя покрива равнините и тайнствена бяла мъгла покрива земята!… Чакай малко. Тайнствена мъгла? „О, не“ — казва загрижено <strong>InspectorCaracal</strong> — „изглежда някакъв дух причинява мъглата. О, и се е засилил право насам.“",
"questGhostStagCompletion": "Духът, привидно незасегнат, свежда глава. Успокояващ глас обгръща групата ви: „Извинявам се за държанието си. Току-що се събудих от дрямка и изглежда още не съм с всичкия си. Моля, вземете ги в знак на извинение.“ — няколко яйца се появяват в тревата пред духа. Без да каже дума, духът се стрелва към гората, а от гърба му се сипят цветя.",
"questGhostStagBoss": "Призрачен елен",
"questGhostStagDropDeerEgg": "Елен (яйце)",
"questGhostStagUnlockText": "Отключва възможността за купуване на яйца на елен от пазара.",
"questGhostStagUnlockText": "Отключва възможността за купуване на Яйца на елен от Пазара",
"questRatText": "Цар Плъх",
"questRatNotes": "Боклук! Огромни купчини неотметнати ежедневни задачи се търкалят навсякъде из Хабитика. Проблемът е станал толкова сериозен, че вече навсякъде са плъзнали пълчища от плъхове. Забелязваш @Pandah да гали нежно едно от животните. Тя обяснява, че плъховете са мили същества, които се хранят с неизпълнени ежедневни задачи. Истинският проблем е, че ежедневните задачи са паднали в канализацията, създаващи опасна яма, която трябва да бъде прочистена. Докато слизаш в канализацията, един огромен плъх с кървавочервени очи и разкривени жълти зъби те напада, за да защити себеподобните си. Ще избягаш ли от страх, или ще се изправиш срещу митичния цар Плъх?",
"questRatCompletion": "Последният ти удар източва силата на огромния плъх; очите му избледняват и стават сиви. Звярът се разпада на множество малки плъхове, които се изпокриват от страх. Забелязваш, че @Pandah стои зад теб и наблюдава могъщото някога създание. Тя обяснява, че жителите на Хабитика са вдъхновени от смелостта ти и бързо изпълняват всички свои незавършени ежедневни задачи. Предупреждава те, че всички трябва да бъдем бдителни, защото отслабим ли защитата си, цар Плъх ще се завърне. Като отплата, @Pandah ти предлага няколко яйца на плъх. Забелязвайки неспокойното ти изражение, тя се усмихва: „Те са страхотни любимци.“",
"questRatBoss": "Цар Плъх",
"questRatDropRatEgg": "Плъх (яйце)",
"questRatUnlockText": "Отключва възможността за купуване на яйца на плъх от пазара.",
"questRatUnlockText": "Отключва възможността за купуване на Яйца на Плъх от Пазара",
"questOctopusText": "Зовът на Октотулу",
"questOctopusNotes": "@Urse, млад книжник със странен поглед, те е помолил да проучите загадъчна пещера близо до морския бряг. Между низините в скалите, където се събира приливна вода, откривате огромен портал от сталактити и сталагмити. Докато приближавате портала, забелязвате как в основата му се образува тъмен водовъртеж. Погледите ви замръзват, когато наподобяващ октопод дракон се появява от дълбините. „Лепкавото изчадие на звездите се е пробудило“ — изревава бясно @Urse. — „След милиарди години, великият Октотулу е отново на свобода, и може да опустошава и плячкосва на воля!“",
"questOctopusCompletion": "С един последен удар, съществото се плъзва обратно във водовъртежа, откъдето се появи. Не можеш да разбереш дали @Urse се радва на победата или е тъжен, понеже създанието си отива. Безмълвен, спътникът ти посочва три огромни, слизести яйца в един от близките приливни басейни. „Сигурно са просто яйца на октопод“ — казваш притеснено ти. Докато се връщате към дома, @Urse трескаво пише в дневника си, а ти си помисляш, че това няма да е последният път, когато чуваш името на великия Октотулу.",
"questOctopusBoss": "Октотулу",
"questOctopusDropOctopusEgg": "Октопод (яйце)",
"questOctopusUnlockText": "Отключва възможността за купуване на яйца на октопод от пазара.",
"questOctopusUnlockText": "Отключва възможността за купуване на Яйца на Октопод от пазара",
"questHarpyText": "Помощ! Харпия!",
"questHarpyNotes": "Смелият приключенец @UncommonCriminal е изчезнал в гората, следвайки следите на крилато чудовище, което било забелязано преди няколко дни. Докато се подготвяш да започнеш търсенето му, един ранен папагал каца на ръката ти; а през красивата му перушина се вижда грозен белег. За крака му има закачена бележка, от която разбираш, че докато защитавал папагалите, @UncommonCriminal бил хванат от зла харпия, и отчаяно се нуждае от помощта ти, за да се измъкне. Ще последваш ли птицата, за да победиш харпията и спасиш @UncommonCriminal?",
"questHarpyCompletion": "Последният удар поваля харпията; перата ѝ се разхвърчават във всички посоки. След като бързо се качваш в гнездото ѝ, намираш @UncommonCriminal, обграден с папагалски яйца. Двамата заедно бързо връщате яйцата в близките гнезда. Белязаният папагал, който те извести, изграчва силно и пуска няколко яйца в ръцете ти. „След нападението на харпията няколко яйца имат нужда от нов дом“ — обяснява @UncommonCriminal. — „Изглежда си избран за почетен папагал.“",
"questHarpyBoss": "Харпия",
"questHarpyDropParrotEgg": "Папагал (яйце)",
"questHarpyUnlockText": "Отключва възможността за купуване на яйца на папагал от пазара.",
"questHarpyUnlockText": "Отключва възможността за купуване на Яйца на папагал от Пазара",
"questRoosterText": "Петльова ярост",
"questRoosterNotes": "От години фермерът @extrajordanary използва петли вместо будилник. Но сега се е появил огромен петел, който кукурига по-силно от всички останали; и събужда всички в Хабитика! Лишените от сън хабитиканци се затрудняват с изпълнението на ежедневните си задачи. @Pandoro решава, че е дошло време да сложи край на това: „Има ли някой, който да научи този петел да кукурига по-тихо?“. Ти изявяваш желание и отиваш при петела рано една сутрин… но той се завърта, размахва огромните си крила, показва острите си нокти и надава боен вик.",
"questRoosterCompletion": "С изтънченост и сила, ти успя да укротиш дивия звяр. Ушите му, доскоро пълни с пера и полузабравени задачи, сега са напълно прочистени. Сега той кукурига тихо, галейки рамото ти с клюна си. На следващия ден се подготвяш да поемеш обратно към дома, когато @EmeraldOx дотичва при теб с една покрита кошница: „Почакай! Когато отидох във фермата тази сутрин, открих, че петелът е избутал тези до вратата, където ти спеше. Мисля, че иска да ги вземеш.“ Ти отваряш кошницата и виждаш в нея три красиви яйца.",
"questRoosterBoss": "Петел",
"questRoosterDropRoosterEgg": "Петел (яйце)",
"questRoosterUnlockText": "Отключва възможността за купуване на яйца на петел от пазара.",
"questRoosterUnlockText": "Отключва възможността за купуване на яйца на петел от пазара",
"questSpiderText": "Леденият паяк",
"questSpiderNotes": "Със захлаждането на времето, нежен скреж започва да се появява на прозорците на хабитиканците, като дантелена мрежа… с изключение на прозорците на @Arcosine, които са напълно замразени от ледения паяк, който в момента живее в дома му. О, Боже.",
"questSpiderCompletion": "Леденият паяк се сгромолясва, оставяйки след себе си малка купчина скреж и няколко от магическите си пашкули с яйца. @Arcosine бързо ти ги предлага като награда — може би ти би могъл да отгледаш по-безопасни паяци като любимци?",
"questSpiderBoss": "Паяк",
"questSpiderDropSpiderEgg": "Паяк (Яйце)",
"questSpiderUnlockText": "Отключва възможността за купуване на яйца на паяк от пазара.",
"questSpiderUnlockText": "Отключва възможността за купуване на Яйца на Паяк от Пазара",
"questGroupVice": "Порок, змеят на сенките",
"questVice1Text": "Порок, част 1: Освободете се от влиянието на дракона",
"questVice1Notes": "<p>Говори се, че в пещерите на планината Хабитика живее отвратително зло. Чудовище, чието присъствие изкривява волята на силните герои по тези земи, повличайки ги към лоши навици и мързел! Звярът е огромен дракон с невъобразима сила и е изграден от сенки: това е Порокът, коварният змей на сенките. Смели хабитиканци, изправете се и победете този нечист звяр веднъж и завинаги, но само ако вярвате, че можете да удържите огромната му мощ.</p><h3>Порок, част 1: </h3><p>Как ще се биете със звяра, ако той вече има власт над вас? Не попадайте в хватката на мързела и пороците! Работете здраво, за да се преборите с тъмното влияние на дракона и се отскубнете от хватката му!</p>",
@@ -137,7 +137,7 @@
"questSeahorseCompletion": "Опитоменият морски жребец плува покорно до теб. „О, вижте! — казва Kiwibot — „Той иска да се погрижим за децата му“ — и тя ти дава три яйца. — „Гледай ги добре“ — казва тя. — „Добре дошъл си в Мудноград по всяко време!“",
"questSeahorseBoss": "Морски жребец",
"questSeahorseDropSeahorseEgg": "Морско конче (яйце)",
"questSeahorseUnlockText": "Отключва възможността за купуване на яйца на морско конче от пазара.",
"questSeahorseUnlockText": "Отключва възможността за купуване на Яйца на Морско Конче от Пазара",
"questGroupAtom": "Атаката на Баналността",
"questAtom1Text": "Атаката на Баналността, част 1: Чиниено бедствие!",
"questAtom1Notes": "Достигаш бреговете на Отмитото езеро и решаваш да си починеш… но езерото е пълно с неизмити чинии! Как ли е станало това? Е, не можеш да оставиш езерото в това състояние. Има само едно нещо, което можеш да направиш: да измиеш чиниите и да спасиш мястото си за почивка! По-добре потърси сапун за тази бъркотия, и нека да е много…",
@@ -159,13 +159,13 @@
"questOwlCompletion": "Нощният бухал със зората избледнява,<br>а теб умората най-после побеждава.<br>Може би е време за топлото легло?<br>Но върху него намираш малко гнездо!<br>Нощният бухал знае, че не се боиш<br>да свърши работа и до късно да стоиш.<br>Но новите ти любимци, щом тъмно стане вън,<br>с писукане ще ти казват, че време е за сън.",
"questOwlBoss": "Нощният бухал",
"questOwlDropOwlEgg": "Бухал (яйце)",
"questOwlUnlockText": "Отключва възможността за купуване на яйца на бухал от пазара.",
"questOwlUnlockText": "Отключва възможността за купуване на Яйца на Бухал от Пазара",
"questPenguinText": "Пернатият мраз",
"questPenguinNotes": "Въпреки че в най-южния край на Хабитика сега е лято, необичаен студ е сковал Веселото езеро. Силни, смразяващи ветрове духат навсякъде, а брегът започва да замръзва. От земята се подават ледени шипове, избутващи тревата и пръстта настрани. @Melynnrose и @Breadstrings дотичват при теб.<br><br>„Помощ!“ — казва @Melynnrose — „Докарахме огромен пингвин, който да замрази езерото, за да можем да се пързаляме по него, но рибата ни свърши и няма с какво да го храним!“<br><br>„Той се ядоса и използва ледения си дъх върху всичко, което очите му видят!“ — казва @Breadstrings — „Моля те, трябва да го усмириш, преди да покрие всички ни с лед!“ Изглежда трябва да накараш пингвина да… <em>охлади страстите.</em>",
"questPenguinCompletion": "След победата над пингвина, ледът се стопява. Огромният пингвин се успокоява на жаркото слънце и си похапва от една кофа с риба, която ти намери. Той се пързаля по езерото и духа внимателно надолу, за да образува гладък, лъщящ лед. Колко странна птица! „Изглежда е оставил тук няколко яйца“ — казва @Painter de Cluster. <br><br>@Rattify се смее: „Може би тези пингвини ще бъдат малко… по-топли към теб?“",
"questPenguinBoss": "Леден пингвин",
"questPenguinDropPenguinEgg": "Пингвин (яйце)",
"questPenguinUnlockText": "Отключва възможността за купуване на яйца на пингвин от пазара.",
"questPenguinUnlockText": "Отключва възможността за купуване на Яйца на Пингвин от Пазара",
"questStressbeastText": "Противното стресиращо чудовище на Споколандските степи",
"questStressbeastNotes": "Изпълнявайте ежедневните и обикновените си задачи, за да нападате световния главатар! Неизпълнените ежедневни задачи допринасят за нарастването на лентата за Стресиращ удар. Когато тя се напълни, световният главатар ще нападне някой компютърен персонаж. Той никога няма да нанесе щети на отделни играчи или акаунти по никакъв начин. Броят се само неизпълнените ежедневни задачи на акаунтите, които не почиват в странноприемницата.<br><br>~*~<br><br>Първото нещо, което се чува, са стъпките: по-бавни и по-силни от тези при паническо бягство. Един по един, хабитиканците се подават през вратите си и остават безмълвни.<br><br>Всички сме виждали стресиращи чудовища и преди, разбира се — мънички, порочни създания, които нападат в тежки времена. Но нещо като това? Това тяло, по-високо от сградите, с лапи, които могат с лекота да смачкат дракон. От вонящата му кожа излиза студена пара, а ревът му причинява ледена вълна, която отнася покривите на къщите ни. Чудовище с такъв размер е споменавано само в старите легенди.<br><br>„Внимавайте, хабитиканци!“ — изревава SabreCat — „Барикадирайте се в домовете си — това е Противното стресиращо чудовище!“<br><br>„Това нещо трябва да е създадено от векове стрес!“ — казва Kiwibot, докато заключва здраво вратата на кръчмата и залоства прозорците.<br><br>„Споколандските степи“ — казва Lemoness с мрачно лице, — „през цялото това време мислехме, че те са кротки и безгрижни, но явно тайно са криели стреса си някъде. През поколенията той се е превърнал в това, а сега се е освободило и ги е нападнало, а сега — и нас!“<br><br>Единственият начин да прогоним стресиращото чудовище, било то противно или не, е да го нападнем с изпълнени ежедневни и обикновени задачи! Нека се обединим и да се преборим с този страховит противник — но внимавайте да не протакате задачите си и да изпълнявате ежедневните си задачи, защото в противен случай чудовището може да се вбеси толкова много, че да ни нападне с…",
"questStressbeastBoss": "Противното стресиращо чудовище",
@@ -191,19 +191,19 @@
"questTRexUndeadRageDescription": "Тази лента се запълва, когато не изпълнявате ежедневните си задачи. Когато се запълни докрай, скелетният тиранозавър ще излекува 30% от оставащото си здраве!",
"questTRexUndeadRageEffect": "`Скелетният тиранозавър използва СКЕЛЕТНО ИЗЦЕЛЕНИЕ!`\n\nЧудовището надава неземен рев и част от увредените му кости се свързват отново!",
"questTRexDropTRexEgg": "Тиранозавър (яйце)",
"questTRexUnlockText": "Отключва възможността за купуване на яйца на тиранозавър от пазара.",
"questTRexUnlockText": "Отключва възможността за купуване на Яйца на Тиранозавър от Пазара",
"questRockText": "Бягство от пещерното създание",
"questRockNotes": "Докато правите преход през Извитите планини с няколко приятели, една вечер правите лагера си в красива пещера с блестящи минерали по стените. Но когато на следващата сутрин се събуждате, откривате, че входът на пещерата е изчезнал, а подът ѝ се движи под краката ви.<br><br>„Планината е жива!“ — крещи @pfeffernusse — „Това не са кристали, това са зъби!“<br><br>@Painter de Cluster хваща ръката ти — „Ще трябва да намерим друг път за излизане. Стойте с мен и не се разсейвайте или може да останем тук завинаги!“",
"questRockBoss": "Кристален колос",
"questRockCompletion": "Усърдието ви позволи да откриете безопасен път през живата планина. Докато стоите на слънцето, твоят приятел @intune забелязва нещо да проблясва близо до изхода на пещерата. Ти се навеждаш, за да го вземеш, и установяваш, че това е малка скала, през която минава златна жилка. Около нея има още подобни скалички в странни форми. Те почти приличат на… яйца?",
"questRockDropRockEgg": "Камък (яйце)",
"questRockUnlockText": "Отключва възможността за купуване на яйца на камък от пазара.",
"questRockUnlockText": "Отключва възможността за купуване на Яйца на Камък от Пазара",
"questBunnyText": "Зайчето-убиец",
"questBunnyNotes": "След много дни, изпълнени с трудности, най-после достигаш върха на планината Отлагане и заставаш пред величествените порти на Крепостта на пренебрежението. Прочиташ издълбания надпис на камъка: „Вътре живеят същества, въплъщение на най-големите ти страхове, причина за бездействието ти. Почукай и се срещни със своите демони!“ Потреперваш, представяйки си ужаса вътре, и усещаш порив да избягаш — така, както ти се е случвало многократно досега. @Draayder те задържа: „Смелост, приятелю! Най-накрая удари твоя час. Трябва да го направиш!“<br><br>Почукваш и портата се отваря навътре. От мрака чуваш оглушителен рев и изваждаш оръжието си.",
"questBunnyBoss": "Зайче-убиец",
"questBunnyCompletion": "С един последен удар зайчето-убиец пада на земята. Искряща мъгла се надига от тялото ѝ, докато тя се смалява до мъничко зайче… нищо подобно на жестокото чудовище, срещу което беше изправен допреди малко. Носът ѝ мърда жизнерадостно и тя заподскача нанякъде, оставяйки няколко яйца след себе си. @Gully се смее: „Планината Отлагане има начин да направи така, че дори най-дребните предизвикателства да изглеждат непреодолими. Да съберем тези яйца и да се връщаме вкъщи!“",
"questBunnyDropBunnyEgg": "Зайче (яйце)",
"questBunnyUnlockText": "Отключва възможността за купуване на яйца на зайче от пазара.",
"questBunnyUnlockText": "Отключва възможността за купуване на Яйца на Зайче от Пазара",
"questSlimeText": "Желираният регент",
"questSlimeNotes": "Докато работиш върху задачите си, забелязваш, че се движиш все по-бавно. „Все едно ходиш в петмез“ — промърморва @Leephon. „Не, все едно ходиш в желе!“ — казва @starsystemic — „Слизестият Желиран регент се е размазал из цяла Хабитика. Задръства всичко и всички се забавят.“ Ти се оглеждаш. Улиците бавно се пълнят с чиста, цветна кал, а хабитиканците се затрудняват с изпълнението на задачите си. Докато останалите се разбягват, ти хващаш един парцал и се приготвяш за битка!",
"questSlimeBoss": "Желиран регент",
@@ -634,5 +634,6 @@
"questVelociraptorCompletion": "Впускаш се през тревата и се изправяш срещу Велоси-рапъра.<br><br><em>Виж какво, рапъре, ти не си издънка,<br>добрите навици за теб не са спънка!<br>Ежедневните си отметни веднага,<br>за някакъв си един ден недей се стяга!</em><br><br>Изпълнен с нова увереност, той зарязва жалването и си тръгва, оставяйки след себе си три яйца.",
"questVelociraptorBoss": "Велоси-рапърът",
"questVelociraptorDropVelociraptorEgg": "Велоцираптор (яйце)",
"questVelociraptorUnlockText": "Отключва възможността за купуване на яйца на велоцираптор от пазара."
"questVelociraptorUnlockText": "Отключва възможността за купуване на яйца на велоцираптор от пазара.",
"evilSantaAddlNotes": "Забележете, че ловджията дядо Коледа и Намери Мечето възнаграждават натрупващо се постижение от мисия, но дават рядко животно и оседлан звяр които могат да бъдат добавени в конюшнята само веднъж."
}

View File

@@ -156,7 +156,7 @@
"optOutOfClasses": "Opt Out",
"optOutOfPMs": "Opt Out",
"chooseClass": "Choose your Class",
"chooseClassLearnMarkdown": "[Learn more about Habitica's class system](http://habitica.fandom.com/wiki/Class_System)",
"chooseClassLearnMarkdown": "[Learn more about Habitica's class system](https://habitica.fandom.com/wiki/Class_System)",
"optOutOfClassesText": "Can't be bothered with classes? Want to choose later? Opt out - you'll be a warrior with no special abilities. You can read about the class system later on the wiki and enable classes at any time under User Icon > Settings.",
"selectClass": "Select <%= heroClass %>",
"select": "Select",

View File

@@ -6,7 +6,7 @@
"commGuideHeadingWelcome": "Welcome to Habitica!",
"commGuidePara001": "Greetings, adventurer! Welcome to Habitica, the land of productivity, healthy living, and the occasional rampaging gryphon. We have a cheerful community full of helpful people supporting each other on their way to self-improvement. To fit in, all it takes is a positive attitude, a respectful manner, and the understanding that everyone has different skills and limitations -- including you! Habiticans are patient with one another and try to help whenever they can.",
"commGuidePara002": "To help keep everyone safe, happy, and productive in the community, we do have some guidelines. We have carefully crafted them to make them as friendly and easy-to-read as possible. Please take the time to read them before you start chatting.",
"commGuidePara003": "These rules apply to all of the social spaces we use, including (but not necessarily limited to) Trello, GitHub, Transifex, and the Wikia (aka wiki). Sometimes, unforeseen situations will arise, like a new source of conflict or a vicious necromancer. When this happens, the mods may respond by editing these guidelines to keep the community safe from new threats. Fear not: you will be notified by an announcement from Bailey if the guidelines change.",
"commGuidePara003": "These rules apply to all of the social spaces we use, including (but not necessarily limited to) Trello, GitHub, Weblate, and the Wikia (aka wiki). Sometimes, unforeseen situations will arise, like a new source of conflict or a vicious necromancer. When this happens, the mods may respond by editing these guidelines to keep the community safe from new threats. Fear not: you will be notified by an announcement from Bailey if the guidelines change.",
"commGuidePara004": "Now ready your quills and scrolls for note-taking, and let's get started!",
"commGuideHeadingInteractions": "Interactions in Habitica",
"commGuidePara015": "Habitica has two kinds of social spaces: public, and private. Public spaces include the Tavern, Public Guilds, GitHub, Trello, and the Wiki. Private spaces are Private Guilds, Party chat, and Private Messages. All Display Names must comply with the public space guidelines. To change your Display Name, go on the website to User > Profile and click on the \"Edit\" button.",

View File

@@ -59,7 +59,7 @@
"schoolDailyText": "Finish homework",
"schoolDailyNotes": "Tap to choose your homework schedule!",
"schoolTodoText": "Finish assignment for class",
"schoolTodoNotes": "Tap to name the assignment and choose a due date!]",
"schoolTodoNotes": "Tap to name the assignment and choose a due date!",
"selfCareHabit": "Take a short break",
"selfCareDailyText": "5 minutes of quiet breathing",

View File

@@ -31,7 +31,7 @@
"weaponWarrior1Text": "Sword",
"weaponWarrior1Notes": "Common soldier's blade. Increases Strength by <%= str %>.",
"weaponWarrior2Text": "Axe",
"weaponWarrior2Notes": "Double-bitted chopping weapon. Increases Strength by <%= str %>",
"weaponWarrior2Notes": "Double-bitted chopping weapon. Increases Strength by <%= str %>.",
"weaponWarrior3Text": "Morning Star",
"weaponWarrior3Notes": "Heavy club with brutal spikes. Increases Strength by <%= str %>.",
"weaponWarrior4Text": "Sapphire Blade",
@@ -435,7 +435,7 @@
"weaponArmoireWeaversCombText": "Weaver's Comb",
"weaponArmoireWeaversCombNotes": "Use this comb to pack your weft threads together to make a tightly woven fabric. Increases Perception by <%= per %> and Strength by <%= str %>. Enchanted Armoire: Weaver Set (Item 2 of 3).",
"weaponArmoireLamplighterText": "Lamplighter",
"weaponArmoireLamplighterNotes": "This long pole has a wick on one end for lighting lamps, and a hook on the other end for putting them out. Increases Constitution by <%= con %> and Perception by <%= per %>. Enchanted Armoire: Lamplighter's Set (Item 1 of 4)",
"weaponArmoireLamplighterNotes": "This long pole has a wick on one end for lighting lamps, and a hook on the other end for putting them out. Increases Constitution by <%= con %> and Perception by <%= per %>. Enchanted Armoire: Lamplighter's Set (Item 1 of 4).",
"weaponArmoireCoachDriversWhipText": "Coach Driver's Whip",
"weaponArmoireCoachDriversWhipNotes": "Your steeds know what they're doing, so this whip is just for show (and the neat snapping sound!). Increases Intelligence by <%= int %> and Strength by <%= str %>. Enchanted Armoire: Coach Driver Set (Item 3 of 3).",
"weaponArmoireScepterOfDiamondsText": "Scepter of Diamonds",
@@ -1837,7 +1837,7 @@
"shieldSpecialSummer2019WarriorText": "Half-Shell Shield",
"shieldSpecialSummer2019WarriorNotes": "Turtle up behind this hefty round shield, etched in the pattern of your favorite reptile's back. Increases Constitution by <%= con %>. Limited Edition 2019 Summer Gear.",
"shieldSpecialSummer2019HealerText": "Conch Trumpet",
"shieldSpecialSummer2019HealerNotes": "Let those who need help know you're coming with the loud blast of this shell trumpet. Limited Edition 2019 Summer Gear. Increases Constitution by 9. ",
"shieldSpecialSummer2019HealerNotes": "Let those who need help know you're coming with the loud blast of this shell trumpet. Increases Constitution by <%= con %>. Limited Edition 2019 Summer Gear.",
"shieldSpecialSummer2019MageText": "Drops of Pure Water",
"shieldSpecialSummer2019MageNotes": "Sweating in the summer sun? No! Performing a simple elemental conjuration to fill the lily pond. Increases Perception by <%= per %>. Limited Edition 2019 Summer Gear.",

View File

@@ -157,7 +157,7 @@
"notAuthorizedToSendMessageToThisUser": "You can't send a message to this player because they have chosen to block messages.",
"blockedToSendToThisUser": "You can't send to this player because you have blocked this player.",
"privateMessageGiftGemsMessage": "Hello <%= receiverName %>, <%= senderName %> has sent you <%= gemAmount %> gems!",
"privateMessageGiftSubscriptionMessage": "<%= numberOfMonths %> months of subscription! ",
"privateMessageGiftSubscriptionMessage": "<%= numberOfMonths %> months of subscription!",
"cannotSendGemsToYourself": "Cannot send gems to yourself. Try a subscription instead.",
"badAmountOfGemsToSend": "Amount must be within 1 and your current number of gems.",
"report": "Report",
@@ -247,7 +247,7 @@
"cannotInviteSelfToGroup": "You cannot invite yourself to a group.",
"userAlreadyInvitedToGroup": "UserID: <%= userId %>, User \"<%= username %>\" already invited to that group.",
"userAlreadyPendingInvitation": "UserID: <%= userId %>, User \"<%= username %>\" already pending invitation.",
"userAlreadyInAParty": "UserID: <%= userId %>, User \"<%= username %>\" already in a party. ",
"userAlreadyInAParty": "UserID: <%= userId %>, User \"<%= username %>\" already in a party.",
"userWithIDNotFound": "User with id \"<%= userId %>\" not found.",
"userWithUsernameNotFound": "User with username \"<%= username %>\" not found.",
"usernameOrUserId": "Username or User ID",

View File

@@ -72,8 +72,8 @@
"scrollsText1": "Quests require parties. If you want to quest solo,",
"scrollsText2": "create an empty party",
"scrollsPre": "You haven't unlocked this quest yet!",
"alreadyEarnedQuestLevel": "You already earned this quest by attaining Level <%= level %>. ",
"alreadyEarnedQuestReward": "You already earned this quest by completing <%= priorQuest %>. ",
"alreadyEarnedQuestLevel": "You already earned this quest by attaining Level <%= level %>.",
"alreadyEarnedQuestReward": "You already earned this quest by completing <%= priorQuest %>.",
"completedQuests": "Completed the following quests",
"mustComplete": "You must first complete <%= quest %>.",
"mustLevel": "You must be level <%= level %> to begin this quest.",

View File

@@ -10,7 +10,7 @@
"spellWizardFrostText": "Chilling Frost",
"spellWizardFrostNotes": "With one cast, ice freezes all your streaks so they won't reset to zero tomorrow! ",
"spellWizardFrostAlreadyCast": " You have already cast this today. Your streaks are frozen, and there's no need to cast this again.",
"spellWizardFrostAlreadyCast": "You have already cast this today. Your streaks are frozen, and there's no need to cast this again.",
"spellWarriorSmashText": "Brutal Smash",
@@ -37,7 +37,7 @@
"spellRogueStealthText": "Stealth",
"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)",
"spellRogueStealthDaliesAvoided": "<%= originalText %> Number of dailies avoided: <%= 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": "Healing Light",
"spellHealerHealNotes": "Shining light restores your health! (Based on: CON and INT)",

View File

@@ -199,7 +199,7 @@
"monthlyRepeatHelpContent": "This task will be due every X months",
"yearlyRepeatHelpContent": "This task will be due every X years",
"resets": "Resets",
"summaryStart": "Repeats <%= frequency %> every <%= everyX %> <%= frequencyPlural %> ",
"summaryStart": "Repeats <%= frequency %> every <%= everyX %> <%= frequencyPlural %>",
"nextDue": "Next Due Dates",
"checkOffYesterDailies": "Check off any Dailies you did yesterday:",
"yesterDailiesTitle": "You left these Dailies unchecked yesterday! Do you want to check off any of them now?",

View File

@@ -49,7 +49,7 @@
"onboardingProgress": "<%= percentage %> % progreso",
"gettingStartedDesc": "Vamos a crear una tarea, complétala y mira tus recompensas. ¡Ganarás <strong>5 logros</strong> y <strong class=\"gold-amount\">100 de oro</strong> cuando hayas terminado!",
"achievementCreatedTaskText": "Creaó su primera tarea.",
"achievementCreatedTask": "Crea una tarea",
"achievementCreatedTask": "¡Crea tu primera tarea!",
"achievementFedPet": "Alimenta a una mascota",
"achievementFedPetModalText": "Hay muchos tipos de comida, pero las mascotas pueden ser quisquillosas",
"achievementHatchedPetModalText": "Dirígete a tu inventario y prueba a combinar una poción de eclosión y un huevo",
@@ -71,5 +71,6 @@
"foundNewItemsExplanation": "Completar tareas te da la oportunidad de encontrar elementos, como Huevos, Pociones para Incubar y Comida para Mascotas.",
"foundNewItems": "¡Encontraste nuevos artículos!",
"onboardingCompleteDescSmall": "¡Si quieres aún más, mira Logros y comienza a coleccionar!",
"yourProgress": "Su Progreso"
"yourProgress": "Su Progreso",
"onboardingComplete": "¡Has completado tus tareas de incorporación!"
}

View File

@@ -1913,5 +1913,7 @@
"weaponSpecialSpring2020HealerNotes": "Un iris es hermoso, pero las hojas son como espadas ... ¡no te dejes engañar por las flores, este bastón es duro como el acero! Aumenta la inteligencia en <% = int%>. Edición limitada 2020 Spring Gear.",
"weaponSpecialSpring2020MageText": "Gotas de lluvia",
"weaponSpecialSpring2020RogueText": "Cuchilla de lazurita",
"weaponArmoireLivelyMatchNotes": "When you're holding this, you're sure to spark someone's interest! Increases Strength by <%= str %>. Enchanted Armoire: Match Maker Set (Item 3 of 4)."
"weaponArmoireLivelyMatchNotes": "When you're holding this, you're sure to spark someone's interest! Increases Strength by <%= str %>. Enchanted Armoire: Match Maker Set (Item 3 of 4).",
"weaponSpecialSpring2020WarriorText": "Ala afilada",
"weaponSpecialSpring2020RogueNotes": "¡Golpearás tan rápido que se verá aún más azul! Aumenta la fuerza en <% = str%>. Edición limitada 2020 Spring Gear."
}

View File

@@ -227,6 +227,6 @@
"pts": "pts",
"chatCastSpellUser": "<%= username %> casts <%= spell %> sa <%= target %>.",
"chatCastSpellParty": "<%= username %> casts <%= spell %> para sa partido.",
"purchasePetItemConfirm": "Ang pagbiling ito ay lalagpas sa bilang ng kagamitang kailangan mo upang ma-hatch ang mga posibleng <%= itemText %> alaga. Sigurado ka ba?",
"purchasePetItemConfirm": "Ang pagbiling nito ay lalagpas sa bilang ng kagamitang kailangan mo upang ma-hatch ang mga posibleng <%= itemText %> alaga. Sigurado ka ba?",
"purchaseForGold": "Bilhin ng <%= cost %> Ginto?"
}

View File

@@ -24,5 +24,42 @@
"defaultTag4": "School",
"defaultTag5": "Teams",
"defaultTag6": "Chores",
"defaultTag7": "Creativity"
"defaultTag7": "Creativity",
"exerciseTodoText": "Magtakda ng iskedyul ng workout",
"exerciseDailyNotes": "Pindutin upang pumili ng iyong iskedyul at magtiyak ng mga exercises!",
"exerciseDailyText": "Stretching >> Araw-araw na workout routine",
"exerciseHabit": "10 min cardio >> + 10 minutes cardio",
"workTodoProjectNotes": "Pindutin upang tukuyin ang iyong kasalukuyang proyekto + magtakda ng petsa!",
"workTodoProject": "Work project >> Kumpletuhin ang work project",
"workDailyImportantTaskNotes": "Pindutin upang tukuyin ang pinakaimportante mong gawain",
"workDailyImportantTask": "Pinakaimportanteng gawain >> Pinagtrabahuhan ang pinakaimportanteng gawain ngayong araw",
"workHabitMail": "I-proseso ang email",
"defaultHabitNotes": "O alisin mula sa edit screen",
"defaultHabitText": "Pumindot dito upang i-edit ito sa masamang ugali na gusto mong alisin",
"creativityTodoNotes": "Pindutin upang tiyakin ang pangalan ng iyong proyekto",
"creativityTodoText": "Tapusin ang malikhaing proyekto",
"creativityDailyNotes": "Pindutin upang tiyakin ang pangalan ng iyong kasalukuyang proyekto + magtakda ng iskedyul!",
"creativityDailyText": "Pagtrabahuhan ang malikhaing proyekto",
"creativityHabit": "Pag-aralan ang master ng craft >> + Nagsanay ng panibagong malikhain na pamamaraan",
"choresTodoNotes": "Pindutin upang tiyakin ang makalat na lugar!",
"choresTodoText": "Ayusin ang aparador >> Ayusin ang kalat",
"choresDailyNotes": "Pindutin upang pumili ng iyong iskedyul!",
"choresDailyText": "Hugasan ang mga pinggan",
"choresHabit": "10 minutong paglilinis",
"selfCareTodoNotes": "Pindutin upang tiyakin ang planong gagawin!",
"selfCareTodoText": "Sumali sa masayang aktibidad",
"selfCareDailyNotes": "Pindutin upang pumili ng iyong iskedyul!",
"selfCareDailyText": "5 minuto ng tahimik na paghinga",
"selfCareHabit": "Magpahinga",
"schoolTodoNotes": "Pindutin upang pangalanan ang takdang aralin at pumili ng takdang petsa!]",
"schoolTodoText": "Tapusin ang takdang aralin para sa klase",
"schoolDailyNotes": "Pindutin upang pumili ng iyong iskedyul sa takdang aralin!",
"schoolDailyText": "Tapusin ang takdang aralin",
"schoolHabit": "Mag-aral/Mag-procrastinate",
"healthTodoNotes": "Pindutin upang magdagdag ng mga listahan!",
"healthTodoText": "Mag-iskedyul ng check-up >> Mag-brainstorm ng masustansyang pagbabago",
"healthDailyNotes": "Pindutin upang gumawa ng mga pagbabago!",
"healthDailyText": "Mag-floss",
"healthHabit": "Kumain ng Masustansyang/Junk Food",
"exerciseTodoNotes": "Pindutin upang magdagdag ng listahan!"
}

View File

@@ -172,7 +172,7 @@
"messageWroteIn": "<%= user %> wrote in <%= group %>",
"msgPreviewHeading": "Message Preview",
"leaderOnlyChallenges": "Only group leader can create challenges",
"sendGift": "Send Gift",
"sendGift": "Regaluhan",
"inviteFriends": "Invite Friends",
"partyMembersInfo": "Your Party currently has <%= memberCount %> members and <%= invitationCount %> pending invitations. The limit of members in a Party is <%= limitMembers %>. Invitations above this limit cannot be sent.",
"inviteByEmail": "Invite by Email",
@@ -339,7 +339,7 @@
"aboutToJoinCancelledGroupPlan": "You are about to join a group with a canceled plan. You will NOT receive a free subscription.",
"cannotChangeLeaderWithActiveGroupPlan": "You can not change the leader while the group has an active plan.",
"leaderCannotLeaveGroupWithActiveGroup": "A leader can not leave a group while the group has an active plan",
"youHaveGroupPlan": "You have a free subscription because you are a member of a group that has a Group Plan. This will end when you are no longer in the group that has a Group Plan. Any months of extra subscription credit you have will be applied at the end of the Group Plan.",
"youHaveGroupPlan": "Mayroon kang libreng subscription dahil ikaw ay miyembro ng Group Plan. Magtatapos ang subscription mo kapag hindi ka na miyembro ng Group Plan.",
"cancelGroupSub": "Cancel Group Plan",
"confirmCancelGroupPlan": "Are you sure you want to cancel the group plan and remove its benefits from all members, including their free subscriptions?",
"canceledGroupPlan": "Canceled Group Plan",
@@ -479,5 +479,19 @@
"sharedCompletion": "Shared Completion",
"recurringCompletion": "None - Group task does not complete",
"singleCompletion": "Single - Completes when any assigned user finishes",
"allAssignedCompletion": "All - Completes when all assigned users finish"
"allAssignedCompletion": "All - Completes when all assigned users finish",
"groupActivityNotificationTitle": "<%= user %> posted sa <%= group %>",
"suggestedGroup": "Nirekomenda dahil bago ka sa Habitica.",
"taskClaimed": "Tinanggap ni <%= userName %> ang gawain <span class=\"notification-bold\"><%= taskText %></span>.",
"youHaveBeenAssignedTask": "<%= managerName %> ay inatasan ka ng gawain <span class=\"notification-bold\"><%= taskText %></span>.",
"userWithUsernameOrUserIdNotFound": "Ang Username o User ID ay hindi mahanap.",
"usernameOrUserId": "Username o User ID",
"sendGiftToWhom": "Sino ang gusto mong bigyan ng regalo?",
"selectGift": "Pumili ng Regalo",
"pmReported": "Maraming salamat sa pag-ulat ng mensaheng ito.",
"blockedToSendToThisUser": "Hindi ka maaaring magpadala sa manlalarong ito dahil na-block mo ang manlalarong ito.",
"PMUnblockUserToSendMessages": "I-unblock ang user na ito upang magpatuloy na magpadala at tumanggap ng mga mensahe.",
"PMUserDoesNotReceiveMessages": "Ang user na ito ay hindi na tumatanggap ng mga pribadong mensahe",
"PMCanNotReply": "Hindi ka makasagot sa usapang ito",
"PMDisabled": "I-disable ang mga Pribadong Mensahe"
}

View File

@@ -126,5 +126,18 @@
"bossHealth": "<%= currentHealth %> / <%= maxHealth %> Health",
"rageAttack": "Rage Attack:",
"bossRage": "<%= currentRage %> / <%= maxRage %> Rage",
"rageStrikes": "Rage Strikes"
"rageStrikes": "Rage Strikes",
"chatBossDontAttack": "Inaatake ni <%= username %> ang <%= bossName %> nang <%= userDamage %> damage. Hindi umatake ang <%= bossName %>, dahil nirerespeto nito ang katotohanang may mga post-maintenance bugs, at ayaw nitong manakit nang hindi patas. Itutuloy nito ang pag-atake sa susunod!",
"chatBossDamage": "Inaatake ni <%= username %> ang <%= bossName %> nang <%= userDamage %> damage. Inaatake ng <%= bossName %> ang partido nang <%= bossDamage %> damage.",
"chatQuestStarted": "Ang iyong quest, <%= questName %>, ay nagsimula na.",
"questAlreadyStartedFriendly": "Nagsimula na ang quest, pero pwede mo namang maabutan ang susunod!",
"questAlreadyStarted": "Nagsimula na ang quest.",
"questInvitationNotificationInfo": "Inimbitahan kang sumali sa isang quest",
"hatchingPotionQuests": "Quests ng Mahikang Kabal sa Pangingitlog",
"tavernBossTired": "Sinubukang pakawalan ng <%= bossName %> ang <%= rageName %> ngunit masyado itong pagod.",
"chatQuestCancelled": "Kinansela ni <%= username %> ang quest ng partido <%= questName %>.",
"chatQuestAborted": "Inabandona ni <%= username %> ang quest ng partido <%= questName %>.",
"chatItemQuestFinish": "Nahanap ang lahat ng gamit! Natanggap ng partido ang kanilang gantimpala.",
"chatFindItems": "Nakahanap si <%= username %> ng <%= items %>.",
"chatBossDefeated": "Natalo mo ang <%= bossName %>! Natanggap ng mga miyembro ng Questing na partido ang mga gantimpala ng tagumpay."
}

View File

@@ -120,7 +120,7 @@
"giftedSubscriptionFull": "Hello <%= username %>, <%= sender %> has sent you <%= monthCount %> months of subscription!",
"giftedSubscriptionWinterPromo": "Hello <%= username %>, you received <%= monthCount %> months of subscription as part of our holiday gift-giving promotion!",
"invitedParty": "Invited To Party",
"invitedGuild": "Invited To Guild",
"invitedGuild": "Inimbitahan ka sa isang Samahan",
"importantAnnouncements": "Reminders to check in to complete tasks and receive prizes",
"weeklyRecaps": "Summaries of your account activity in the past week (Note: this is currently disabled due to performance issues, but we hope to have this back up and sending e-mails again soon!)",
"onboarding": "Guidance with setting up your Habitica account",
@@ -137,7 +137,7 @@
"unsubscribeAllEmailsText": "By checking this box, I certify that I understand that by unsubscribing from all emails, Habitica will never be able to notify me via email about important changes to the site or my account.",
"unsubscribeAllPush": "Check to Unsubscribe from all Push Notifications",
"correctlyUnsubscribedEmailType": "Correctly unsubscribed from \"<%= emailType %>\" emails.",
"subscriptionRateText": "Recurring $<%= price %> USD every <%= months %> months",
"subscriptionRateText": "Umuulit $<%= price %> USD kada <%= months %> buwan",
"recurringText": "recurring",
"benefits": "Benefits",
"coupon": "Coupon",
@@ -173,14 +173,14 @@
"pushDeviceAlreadyAdded": "The user already has the push device",
"pushDeviceNotFound": "The user has no push device with this id.",
"pushDeviceRemoved": "Push device removed successfully.",
"buyGemsGoldCap": "Cap raised to <%= amount %>",
"buyGemsGoldCap": "Itinaas ang limitasyon hanggang <%= amount %>",
"mysticHourglass": "<%= amount %> Mystic Hourglass",
"mysticHourglassText": "Mystic Hourglasses allow purchasing a previous month's Mystery Item set.",
"purchasedPlanId": "Recurring $<%= price %> USD each <%= months %> Month(s) (<%= plan %>)",
"purchasedPlanExtraMonths": "You have <%= months %> months of extra subscription credit.",
"purchasedPlanExtraMonths": "Mayroon kang <%= months %> buwan ng dagdag subscription credit.",
"consecutiveSubscription": "Consecutive Subscription",
"consecutiveMonths": "Consecutive Months:",
"gemCapExtra": "Gem Cap Extra:",
"gemCapExtra": "Bonus sa Limitasyon ng Hiyas",
"mysticHourglasses": "Mystic Hourglasses:",
"mysticHourglassesTooltip": "Mystic Hourglasses",
"paypal": "PayPal",
@@ -203,6 +203,15 @@
"goToSettings": "Go to Settings",
"usernameVerifiedConfirmation": "Your username, <%= username %>, is confirmed!",
"usernameNotVerified": "Please confirm your username.",
"changeUsernameDisclaimer": "We will be transitioning login names to unique, public usernames soon. This username will be used for invitations, @mentions in chat, and messaging.",
"verifyUsernameVeteranPet": "One of these Veteran Pets will be waiting for you after you've finished confirming!"
"changeUsernameDisclaimer": "Ang username mo ay ginagamit para sa mga imbitasyon, @mentions sa usapan, at sa pagmensahe. Dapat itong 1 hanggang 20 na karakter, na naglalaman lamang ng mga letrang a hanggang z, mga numerong 0 hanggang 9, mga hyphen, o underscore, at hindi maaaring isama ang hindi angkop na mga termino.",
"verifyUsernameVeteranPet": "One of these Veteran Pets will be waiting for you after you've finished confirming!",
"onlyPrivateSpaces": "Sa mga pribadong espasyo lamang",
"everywhere": "Kahit saan",
"suggestMyUsername": "Magrekomenda ng aking username",
"mentioning": "Pagmention",
"buyGemsGoldCapBase": "Limitasyon ng Hiyas hanggang <%= amount %>",
"subscriptionReminders": "Paalala sa Subscriptions",
"newPMNotificationTitle": "Bagong Mensahe mula kay <%= name %>",
"chatExtensionDesc": "Ang mga Chat Extension para sa Habitica ay nagdadagdag ng madaling maunawaang chat box sa lahat ng habitica.com. Pinapayagan nitong magchat ang mga users sa Tavern, kanilang partido, at kahit anong samahang kinabibilangan nila.",
"chatExtension": "<a target='blank' href='https://chrome.google.com/webstore/detail/habitrpg-chat-client/hidkdfgonpoaiannijofifhjidbnilbb'> Chrome Chat Extension</a> at <a target='blank' href='https://addons.mozilla.org/en-US/firefox/addon/habitica-chat-client-2/'> Firefox Chat Extension</a>"
}

View File

@@ -272,8 +272,8 @@
"newEmailRequired": "新しいメールアドレスがありません。",
"usernameTime": "あなたのユーザー名を決める時間です!",
"usernameInfo": "ログイン名は今では固有のユーザー名となり、あなたの表示名のそばに表示されているでしょう。そして、招待、チャットでの@返信、メッセージなどのために使われます。<br><br>もしこの変更についてより詳しく知りたいときは、<a href='http://habitica.fandom.com/wiki/Player_Names' target='_blank'>私たちのwikiをご覧ください</a>。",
"usernameTOSRequirements": "ユーザー名は、私たちの<a href='/static/terms' target='_blank'>サービス利用規約</a>と<a href='/static/community-guidelines' target='_blank'>コミュニティガイドライン</a>を守ったものでなければなりません。もしあなたが以前にログイン名を設定していなかった場合、あなたのユーザー名は自動生成されました。",
"usernameTaken": "そのユーザー名は既に使われています",
"usernameTOSRequirements": "ユーザー名は、私たちの<a href='/static/terms' target='_blank'>サービス利用規約</a>と<a href='/static/community-guidelines' target='_blank'>コミュニティガイドライン</a>を守ったものでなければなりません。もしあなたが以前にログイン名を設定していなかった場合、あなたのユーザー名は自動生成されました。",
"usernameTaken": "そのユーザー名は既に使われています",
"passwordConfirmationMatch": "パスワードが不一致です。",
"invalidLoginCredentials": "ユーザー名とパスワードのいずれかまたは両方が無効です。",
"passwordResetPage": "パスワードをリセットする",

View File

@@ -486,12 +486,12 @@
"taskClaimed": "<%= userName %> は、タスク<span class=\"notification-bold\"><%= taskText %></span>を担当しています。",
"groupActivityNotificationTitle": "<%= user %>が<%= group %>に投稿しました",
"blockedToSendToThisUser": "このユーザーにブロックされているため、メッセージは送れません。",
"PMDisabled": "プライベートメッセージを無効にする",
"PMDisabled": "メッセージを無効にする",
"userWithUsernameOrUserIdNotFound": "ユーザー名またはユーザーIDが見つかりませんでした。",
"usernameOrUserId": "ユーザー名またはユーザーID",
"sendGiftToWhom": "誰に贈り物を送りたいですか?",
"selectGift": "贈り物を選ぶ",
"PMUnblockUserToSendMessages": "メッセージの送信と受信を続けるためにこのユーザーのブロックを解除しましょう。",
"PMUnblockUserToSendMessages": "メッセージの送信と受信を続けるには、このユーザーのブロックを解除しましょう。",
"PMUserDoesNotReceiveMessages": "このユーザーはもはやプライベートメッセージを受信していません",
"PMCanNotReply": "この会話に返信することはできません"
}

View File

@@ -7,7 +7,7 @@
"buyGemsGoldText": "商人のAlexanderが、ジェムを個につき20ゴールドで売ってくれます。Alexanderと1ヵ月の間に取り引きできるジェムの数は、最初は25個に限定されています。しかし、寄付の継続3カ月ごとにその数が5個ずつ増えていき、最大で月に50個を買うことができます",
"mustSubscribeToPurchaseGems": "ゴールドでジェムを買うには、寄付が必要です",
"reachedGoldToGemCap": "ゴールドで買えるジェムの上限が今月で <%= convCap %> に達しました。乱用や不正なジェムの増殖を止めるために上限を設けています。制限は毎月1日から3日のいずれかの時点で解除されます。",
"reachedGoldToGemCapQuantity": "あなたのゴールドからジェムへの交換リクエスト数 <%= quantity %> は、今月の交換上限数 <%= convCap %> を超えてしまっています。乱用や不正なジェムの増殖を止めるために上限を設けています。制限は毎月1日から3日のいずれかの時点で解除されます。",
"reachedGoldToGemCapQuantity": "あなたリクエストした数 <%= quantity %> は、今月中に購入できる上限数を超えています(<%= convCap %>。毎月1日から3日のいずれかの時点で、最大数が利用可能になります。ご寄付いただきありがとうございます",
"retainHistory": "詳細な履歴の保存",
"retainHistoryText": "完了した To-Do やタスクの履歴を、より長期間閲覧可能になります。",
"doubleDrops": "落とし物上限が2倍に",

View File

@@ -3,13 +3,13 @@
"profile": "프로필",
"avatar": "아바타 꾸미기",
"editAvatar": "아바타 수정",
"noDescription": "This Habitican hasn't added a description.",
"noPhoto": "This Habitican hasn't added a photo.",
"noDescription": "이 해비티칸은 설명을 추가하지 않았습니다.",
"noPhoto": "이 해비티칸은 사진을 추가하지 않았습니다.",
"other": "그 외",
"fullName": "이름",
"displayName": "Display name",
"changeDisplayName": "Change Display Name",
"newDisplayName": "New Display Name",
"displayName": "타인에게 보여질 이름",
"changeDisplayName": "( 타인에게 보여지는 )이름 바꾸기",
"newDisplayName": "새로운 이름",
"displayPhoto": "사진",
"displayBlurb": "자기소개",
"displayBlurbPlaceholder": "본인을 소개해주세요",
@@ -227,5 +227,6 @@
"pts": "pts",
"chatCastSpellUser": "<%= username %> 이 <%= spell %> 를<%= target %>에게 시전했습니다.",
"chatCastSpellParty": "<%= username %> 이 파티를 위해 <%= spell %> 를 시전했습니다.",
"purchaseForGold": "<%= cost %> 골드로 구매합니까?"
"purchaseForGold": "<%= cost %> 골드로 구매합니까?",
"purchasePetItemConfirm": "이 구매는 당신이 모든 반려동물을 부화시키기 위한 아이템들의 수를 능가할 것입니다. <%= itemText %> 정말 괜찮겠습니까?"
}

View File

@@ -40,5 +40,17 @@
"forgotPass": "비밀번호를 잊음",
"footerDevs": "개발자들",
"choreSample3": "설거지하기",
"choreSample2": "집안일 20분 하기"
"choreSample2": "집안일 20분 하기",
"featureAchievementHeading": "내가한것들에대한명예배지",
"featureAchievementByline": "존나 멋진 것을 했어? 배지를 가진 다음 자랑해 봐!",
"examplesHeading": "플레이어들은 해비티카를 ... 을 하기 위해 써.",
"evagantzQuote": "내 첫 치과 진료 때 치아위생사가 내 치실 습관을 굉장히 좋아했어. 고마워 [Habitica]!",
"contribUse": "해비티카에 기여한 사람들은 ~ 을 써",
"communityExtensions": "<a href='http://habitica.fandom.com/wiki/Extensions,_Add-Ons,_and_Customizations' target='_blank'>Add-ons & Extensions</a>\n(그냥 링크 같음)",
"communityBug": "버그 제출하기",
"clearBrowserData": "브라우저 데이터 없애기",
"businessSample4": "고객한테 문서 하나 준비해",
"businessSample2": "20분동안 채워",
"autumnesquirrelQuote": "집안일과 해야 할 일을 미루고 있지 않고 돈도 제때 내.",
"andeeliaoQuote": "최고임. 며칠 전에 막 시작했고 시간 관리가 잘 되고 더욱 생산적이게 됨!"
}

View File

@@ -1744,5 +1744,10 @@
"twoHandedItem": "Two-handed item.",
"shieldSpecialKS2019Text": "Scutum Gryphi Mythicis",
"headSpecialKS2019Text": "Cassis Gryphi Mythicis",
"armorSpecialKS2019Text": "Arma Gryphi Mythicis"
"armorSpecialKS2019Text": "Arma Gryphi Mythicis",
"weaponArmoireBambooCaneText": "Ferula Bambusae",
"weaponArmoireSlingshotText": "Funda",
"weaponArmoireJugglingBallsText": "Sphaera Praestigia",
"weaponArmoireVernalTaperText": "Cerneus Vernus",
"weaponSpecialSpring2019RogueText": "Fulmen"
}

View File

@@ -1947,5 +1947,7 @@
"headSpecialPiDayNotes": "Probeer om dit stuk heerlijke taart op je hoofd te balanceren terwijl je in een cirkel wandelt. Of werp het naar een rode Dagtaak! Of je zou het kunnen opeten. Jouw keuze! Kent geen voordeel toe.",
"headMystery201903Text": "Spiegelei Helm",
"armorArmoireBaseballUniformText": "Honkbal Uniform",
"armorArmoireMatchMakersApronText": "Koppelaarsters Schort"
"armorArmoireMatchMakersApronText": "Koppelaarsters Schort",
"armorArmoireBoxArmorText": "Boks Uitrusting",
"headSpecialSummer2019RogueText": "Voorhamer Helm"
}

View File

@@ -1479,7 +1479,7 @@
"shieldArmoireAntiProcrastinationShieldText": "Escudo Anti-Procrastinação",
"shieldArmoireAntiProcrastinationShieldNotes": "Este escudo forte de ferro irá te ajudar a bloquear distrações quando elas se aproximarem. Aumenta a Constituição em <%= con %>. Armário Encantado: Conjunto Anti-Procrastinação (Item 3 de 3).",
"shieldArmoireHorseshoeText": "Ferradura",
"shieldArmoireHorseshoeNotes": "Ajude a proteger os pés de suas montarias com esse sapato de ferro. Aumenta Constituição, Percepção e Força em <%= attrs %> cada. Armário Encantado: Conjunto do Tratador de Cavalos (Item 3 de 3)",
"shieldArmoireHorseshoeNotes": "Ajude a proteger os pés de suas montarias com esse sapato de ferro. Aumenta Constituição, Percepção e Força em <%= attrs %> cada. Armário Encantado: Conjunto do Tratador de Cavalos (Item 3 de 3).",
"shieldArmoireHandmadeCandlestickText": "Castiçal Feito à Mão",
"shieldArmoireHandmadeCandlestickNotes": "Sua cera fina fornece luz e calor para os Habiticanos gratos! Aumenta Força em <%= str %>. Armário Encantado: Conjunto Criador de Castiçais (Item 3 de 3).",
"shieldArmoireWeaversShuttleText": "Transporte do Tecelão",
@@ -1761,7 +1761,7 @@
"headArmoireVernalHenninText": "Hennin Vernal",
"headArmoireVernalHenninNotes": "Mais do que apenas um belo chapéu, este chapéu cônico também pode conter uma lista de tarefas penduradas dentro. Aumenta a Percepção em <%= per %>. Armário Encantado: Conjunto de Vestimentas Vernais (Item 1 de 3).",
"shieldMystery201902Text": "Confete enigmático",
"shieldMystery201902Notes": "Este papel brilhante forma corações mágicos que vagueiam lentamente e dançam no ar. Não confere nenhum benefício. Fevereiro de 2019 Item dos Assinantes",
"shieldMystery201902Notes": "Este papel brilhante forma corações mágicos que vagueiam lentamente e dançam no ar. Não confere nenhum benefício. Item de Assinantes, Fevereiro de 2019.",
"shieldArmoireMightyPizzaText": "Pizza Poderosa",
"shieldArmoireMightyPizzaNotes": "Claro, é um escudo muito bom, mas nós sugerimos fortemente que você coma essa ótima, ótima pizza. Aumenta a Percepção em <%= per %>. Armário Encantado: Conjunto do Chef (Item 4 de 4).",
"eyewearMystery201902Text": "Macara enigmática do crush",

View File

@@ -16,7 +16,7 @@
"randomize": "Aleatorizar",
"mattBoch": "Matt Boch",
"mattShall": "Devo trazer seu corcel, <%= name %>? Uma vez que você alimentou o seu mascote o suficiente para torná-lo uma montaria, ele aparecerá aqui. Clique em uma montaria para montá-la!",
"mattBochText1": "Bem-vindo ao meu Estábulo! Sou Matt, o mestre das bestas. Sempre que você completar uma tarefa, poderá obter, randomicamente, um Ovo ou uma Poção de eclosão para chocar Mascotes. Quando você choca um Mascote no, ele aparecerá aqui! Clique na imagem de um Mascote para adicioná-lo ao seu Avatar. Alimente-os usando a comida que você encontrar e eles se transformarão em poderosas Montarias.",
"mattBochText1": "Bem-vindo(a) ao meu Estábulo! Sou Matt, o mestre das bestas. Sempre que você completar uma tarefa, poderá obter, randomicamente, um Ovo ou uma Poção de eclosão para chocar Mascotes. Quando você choca um Mascote no, ele aparecerá aqui! Clique na imagem de um Mascote para adicioná-lo ao seu Avatar. Alimente-os usando a comida que você encontrar e eles se transformarão em poderosas Montarias.",
"welcomeToTavern": "Boas-vindas à Taverna!",
"sleepDescription": "Precisa de um descanso? Faça uma visita à Pousada do Daniel e pare algumas das mecânicas de jogo mais difíceis do Habitica:",
"sleepBullet1": "Diárias não feitas não lhe causarão dano",

View File

@@ -7,7 +7,7 @@
"buyGemsGoldText": "Alexander, o Mercador, venderá suas Gemas por 20 de ouro cada Gema. Suas remessas mensais serão limitados a 25 Gemas por mês, mas para cada 3 meses consecutivos de assinatura, este limite aumenta em 5 Gemas até o máximo de 50 Gemas por mês!",
"mustSubscribeToPurchaseGems": "É necessário ser assinante para comprar gemas com Ouro.",
"reachedGoldToGemCap": "Você atingiu o limite de<%= convCap %> para conversão de Ouro=>Gemas deste mês. Isto serve para prevenir abusos. O limite irá reiniciar dentro dos três primeiros dias do próximo mês.",
"reachedGoldToGemCapQuantity": "Seu pedido de <%= quantity %> gemas ultrapassa o limite de conversão deste mês (<%= convCap %>). Queremos lhe ver sempre produtivo, e que não farme as gemas em 1 mês. O limite é reiniciado nos primeiros 3 dias de cada mês.",
"reachedGoldToGemCapQuantity": "Seu pedido de <%= quantity %> Gemas ultrapassa o limite que você pode comprar para este mês (<%= convCap %>). O limite fica disponível nos três primeiros dias de cada mês. Obrigado por assinar!",
"retainHistory": "Guardar entradas adicionais no histórico",
"retainHistoryText": "Faz com que o histórico de tarefas e Afazeres completos fiquem disponíveis por mais tempo.",
"doubleDrops": "Capacidade diária de drop duplicada",
@@ -248,5 +248,6 @@
"subscribersReceiveBenefits": "Assinantes recebem esses benefícios úteis!",
"giftASubscription": "Presentar uma Assinatura",
"mysterySet202003": "Conjunto do(a) gladiador(a) farpado(a)",
"mysterySet202004": "Conjunto do(a) Monarca poderoso(a)"
"mysterySet202004": "Conjunto do(a) Monarca poderoso(a)",
"mysterySet202005": "Conjunto da Serpe Maravilhosa"
}

View File

@@ -21,7 +21,7 @@
"buffed": "Получен баф",
"bodyBody": "Тело",
"bodySize": "Телосложение",
"size": "Размер",
"size": "Телосложение",
"bodySlim": "Стройное",
"bodyBroad": "Тучное",
"unlockSet": "Разблокировать набор <%= cost %>",
@@ -47,8 +47,8 @@
"beard": "Борода",
"mustache": "Усы",
"flower": "Цветок",
"accent": "Акцент",
"headband": "Повязка на голове",
"accent": "Аксессуары",
"headband": "Повязка на голову",
"wheelchair": "Инвалидное кресло",
"extra": "Дополнительно",
"basicSkins": "Базовые цвета",

View File

@@ -1508,7 +1508,7 @@
"backCapitalized": "Аксессуар на спину",
"backBase0Text": "Нет аксессуаров на спине",
"backBase0Notes": "Нет аксессуаров на спине.",
"animalTails": "Хвосты животных",
"animalTails": "Звериные хвосты",
"backMystery201402Text": "Золотые крылья",
"backMystery201402Notes": "Перья на этих сияющих крыльях сверкают на солнце! Бонусов не дают. Подарок подписчикам февраля 2014.",
"backMystery201404Text": "Крылья сумеречной бабочки",

View File

@@ -2090,7 +2090,7 @@
"armorArmoireBoxArmorText": "盒装甲",
"weaponArmoirePaperCutterNotes": "这可能看起来并不可怕,但你有没有被纸割伤过吗?增加<%= str %>点力量。魔法衣橱纸骑士套装1/3。",
"weaponArmoirePaperCutterText": "裁纸器",
"headAccessoryMystery202005Notes": "有了这么强大的角,哪一个生物敢挑战你呢? 没有属性加成。2020年5月订阅者物品。",
"headAccessoryMystery202005Notes": "有了这么强大的角哪一个生物敢挑战你呢没有属性加成。2020年5月订阅者物品。",
"headAccessoryMystery202005Text": "美妙的飞龙角",
"backMystery202005Notes": "虽然翅膀有些破烂但它还能让你飞向你的目的地。没有属性加成。2020年5月订阅者物品。",
"backMystery202005Text": "美妙的飞龙翅膀"

View File

@@ -4,21 +4,21 @@ import habiticaMarkdown from 'habitica-markdown';
import { model as User } from '../models/user';
const mentionRegex = /\B@[-\w]+/g;
const codeTokenTypes = ['code_block', 'code_inline', 'fence'];
const ignoreTokenTypes = ['code_block', 'code_inline', 'fence', 'link_open'];
/**
* Container class for text blocks and code blocks combined
* Blocks have the properties `text` and `isCodeBlock`
* Container class for valid text blocks and text blocks that should be ignored.
* Blocks have the properties `text` and `ignore`
*/
class TextWithCodeBlocks {
class TextBlocks {
constructor (blocks) {
this.blocks = blocks;
this.textBlocks = blocks.filter(block => !block.isCodeBlock);
this.allText = this.textBlocks.map(block => block.text).join('\n');
this.validBlocks = blocks.filter(block => !block.ignore);
this.allValidText = this.validBlocks.map(block => block.text).join('\n');
}
transformTextBlocks (transform) {
this.textBlocks.forEach(block => {
transformValidBlocks (transform) {
this.validBlocks.forEach(block => {
block.text = transform(block.text);
});
}
@@ -32,18 +32,31 @@ class TextWithCodeBlocks {
* Since tokens have both order and can be nested until infinite depth,
* use a branching recursive algorithm to maintain order and check all tokens.
*/
function findCodeBlocks (tokens, aggregator) {
const result = aggregator || [];
const [head, ...tail] = tokens;
if (!head) {
return result;
function findIgnoreBlocks (tokens) {
// Links span multiple tokens, so keep local state of whether we're in a link
let inLink = false;
function recursor (ts, result) {
const [head, ...tail] = ts;
if (!head) {
return result;
}
if (!inLink && ignoreTokenTypes.includes(head.type)) {
result.push(head);
}
if (head.type.includes('link')) {
inLink = !inLink;
} else if (inLink && head.type === 'text') {
const linkBlock = result[result.length - 1];
linkBlock.textContents = (linkBlock.textContents || []).concat(head.content);
}
return recursor(tail, head.children ? recursor(head.children, result) : result);
}
if (codeTokenTypes.includes(head.type)) {
result.push(head);
}
return findCodeBlocks(tail, head.children ? findCodeBlocks(head.children, result) : result);
return recursor(tokens, []);
}
/**
@@ -57,7 +70,10 @@ function withOptionalIndentation (content) {
return content.split('\n').map(line => `\\s*${line}`).join('\n');
}
function createCodeBlockRegex ({ content, type, markup }) {
// This is essentially a workaround around the fact that markdown-it doesn't
// provide sourcemap functionality and is the most brittle part of this code.
function toSourceMapRegex (token) {
const { type, content, markup } = token;
const contentRegex = escapeRegExp(content);
let regexStr = '';
@@ -65,42 +81,49 @@ function createCodeBlockRegex ({ content, type, markup }) {
regexStr = withOptionalIndentation(contentRegex);
} else if (type === 'fence') {
regexStr = `\\s*${markup}.*\n${withOptionalIndentation(contentRegex)}\\s*${markup}`;
} else { // type === code_inline
} else if (type === 'code_inline') {
regexStr = `${markup} ?${contentRegex} ?${markup}`;
} else if (type === 'link_open') {
const texts = token.textContents.map(escapeRegExp);
regexStr = markup === 'linkify' || markup === 'autolink' ? texts[0]
: `\\[.*${texts.join('.*')}.*\\]\\(${escapeRegExp(token.attrs[0][1])}\\)`;
} else {
throw new Error(`No source mapping regex defined for ignore blocks of type ${type}`);
}
return new RegExp(regexStr);
return new RegExp(regexStr, 's');
}
/**
* Uses habiticaMarkdown to determine what part of the text are code blocks
* Uses habiticaMarkdown to determine which text blocks should be ignored (links and code blocks)
* according to the specification here: https://spec.commonmark.org/0.29/
*/
function findTextAndCodeBlocks (text) {
function findTextBlocks (text) {
// For token description see https://markdown-it.github.io/markdown-it/#Token
// The second parameter is mandatory even if not used, see
// https://markdown-it.github.io/markdown-it/#MarkdownIt.parse
const tokens = habiticaMarkdown.parse(text, {});
const codeBlocks = findCodeBlocks(tokens);
const ignoreBlockRegexes = findIgnoreBlocks(tokens).map(toSourceMapRegex);
const blocks = [];
let remainingText = text;
codeBlocks.forEach(codeBlock => {
const codeBlockRegex = createCodeBlockRegex(codeBlock);
const match = remainingText.match(codeBlockRegex);
let index = 0;
ignoreBlockRegexes.forEach(regex => {
const targetText = text.substr(index);
const match = targetText.match(regex);
if (match.index) {
blocks.push({ text: remainingText.substr(0, match.index), isCodeBlock: false });
blocks.push({ text: targetText.substr(0, match.index), ignore: false });
}
blocks.push({ text: match[0], isCodeBlock: true });
remainingText = remainingText.substr(match.index + match[0].length);
blocks.push({ text: match[0], ignore: true });
index += match.index + match[0].length;
});
if (remainingText) {
blocks.push({ text: remainingText, isCodeBlock: false });
if (index < text.length) {
blocks.push({ text: text.substr(index), ignore: false });
}
return new TextWithCodeBlocks(blocks);
return new TextBlocks(blocks);
}
/**
@@ -108,11 +131,12 @@ function findTextAndCodeBlocks (text) {
* a link towards the user's profile page.
* - Only works if there are no more that 5 user mentions
* - Skips mentions in code blocks as defined by https://spec.commonmark.org/0.29/
* - Skips mentions in links
*/
export default async function highlightMentions (text) {
const textAndCodeBlocks = findTextAndCodeBlocks(text);
const textBlocks = findTextBlocks(text);
const mentions = textAndCodeBlocks.allText.match(mentionRegex);
const mentions = textBlocks.allValidText.match(mentionRegex);
let members = [];
if (mentions && mentions.length <= 5) {
@@ -127,9 +151,9 @@ export default async function highlightMentions (text) {
const regex = new RegExp(`@${username}(?![\\-\\w])`, 'g');
const replacement = `[@${username}](/profile/${member._id})`;
textAndCodeBlocks.transformTextBlocks(blockText => blockText.replace(regex, replacement));
textBlocks.transformValidBlocks(blockText => blockText.replace(regex, replacement));
});
}
return [textAndCodeBlocks.rebuild(), mentions, members];
return [textBlocks.rebuild(), mentions, members];
}