Merge branch 'develop' into task-difficulty-context-classes
7
.bowerrc
@@ -1,8 +1,3 @@
|
||||
{
|
||||
"directory": "website/public/bower_components",
|
||||
"storage" : {
|
||||
"packages" : ".bower-cache",
|
||||
"registry" : ".bower-registry"
|
||||
},
|
||||
"tmp" : ".bower-tmp"
|
||||
"directory": "website/public/bower_components"
|
||||
}
|
||||
|
||||
3
Vagrantfile
vendored
@@ -5,6 +5,9 @@
|
||||
VAGRANTFILE_API_VERSION = "2"
|
||||
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
config.vm.provider "virtualbox" do |v|
|
||||
v.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/vagrant", "1"]
|
||||
end
|
||||
config.vm.box = "thepeopleseason/habitrpg"
|
||||
config.ssh.forward_agent = true
|
||||
|
||||
|
||||
35
common/dist/scripts/habitrpg-shared.js
vendored
@@ -2924,6 +2924,11 @@ api.questEggs = {
|
||||
text: t('questEggTRexText'),
|
||||
adjective: t('questEggTRexAdjective'),
|
||||
canBuy: false
|
||||
},
|
||||
Rock: {
|
||||
text: t('questEggRockText'),
|
||||
adjective: t('questEggRockAdjective'),
|
||||
canBuy: false
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4307,6 +4312,36 @@ api.quests = {
|
||||
gp: 55,
|
||||
exp: 500
|
||||
}
|
||||
},
|
||||
rock: {
|
||||
text: t('questRockText'),
|
||||
notes: t('questRockNotes'),
|
||||
completion: t('questRockCompletion'),
|
||||
value: 4,
|
||||
boss: {
|
||||
name: t('questRockBoss'),
|
||||
hp: 400,
|
||||
str: 1.5
|
||||
},
|
||||
drop: {
|
||||
items: [
|
||||
{
|
||||
type: 'eggs',
|
||||
key: 'Rock',
|
||||
text: t('questRockDropRockEgg')
|
||||
}, {
|
||||
type: 'eggs',
|
||||
key: 'Rock',
|
||||
text: t('questRockDropRockEgg')
|
||||
}, {
|
||||
type: 'eggs',
|
||||
key: 'Rock',
|
||||
text: t('questRockDropRockEgg')
|
||||
}
|
||||
],
|
||||
gp: 31,
|
||||
exp: 200
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
2
common/dist/sprites/habitrpg-shared.css
vendored
1204
common/dist/sprites/spritesmith0.css
vendored
BIN
common/dist/sprites/spritesmith0.png
vendored
|
Before Width: | Height: | Size: 246 KiB After Width: | Height: | Size: 246 KiB |
1544
common/dist/sprites/spritesmith1.css
vendored
BIN
common/dist/sprites/spritesmith1.png
vendored
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
1336
common/dist/sprites/spritesmith2.css
vendored
BIN
common/dist/sprites/spritesmith2.png
vendored
|
Before Width: | Height: | Size: 187 KiB After Width: | Height: | Size: 187 KiB |
490
common/dist/sprites/spritesmith3.css
vendored
BIN
common/dist/sprites/spritesmith3.png
vendored
|
Before Width: | Height: | Size: 495 KiB After Width: | Height: | Size: 495 KiB |
648
common/dist/sprites/spritesmith4.css
vendored
BIN
common/dist/sprites/spritesmith4.png
vendored
|
Before Width: | Height: | Size: 328 KiB After Width: | Height: | Size: 327 KiB |
4
common/dist/sprites/spritesmith5.css
vendored
@@ -370,7 +370,7 @@
|
||||
width: 105px;
|
||||
height: 105px;
|
||||
}
|
||||
.Mount_Head_Rock-Gold {
|
||||
.Mount_Head_Rock-Golden {
|
||||
background-image: url(spritesmith5.png);
|
||||
background-position: -106px -832px;
|
||||
width: 105px;
|
||||
@@ -1846,7 +1846,7 @@
|
||||
width: 75px;
|
||||
height: 93px;
|
||||
}
|
||||
.Pet-Rock-Gold {
|
||||
.Pet-Rock-Golden {
|
||||
background-image: url(spritesmith5.png);
|
||||
background-position: -1860px -1476px;
|
||||
width: 75px;
|
||||
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
@@ -72,6 +72,9 @@
|
||||
"questEggTRexText": "Tyrannosaur",
|
||||
"questEggTRexAdjective": "tiny-armed",
|
||||
|
||||
"questEggRockText": "Rock",
|
||||
"questEggRockAdjective": "lively",
|
||||
|
||||
"eggNotes": "Find a hatching potion to pour on this egg, and it will hatch into a <%= eggAdjective(locale) %> <%= eggText(locale) %>.",
|
||||
|
||||
"hatchingPotionBase": "Base",
|
||||
|
||||
@@ -22,9 +22,7 @@
|
||||
"party": "Party",
|
||||
"createAParty": "Create A Party",
|
||||
"noPartyText": "You are either not in a party or your party is taking a while to load. You can either create one and invite friends, or if you want to join an existing party, have them enter your Unique User ID below and then come back here to look for the invitation:",
|
||||
"startLFG": "To advertise your new party or find one to join, go to ",
|
||||
"linkLFG": "The Archery",
|
||||
"endLFG": " on the Wiki.",
|
||||
"LFG": "To advertise your new party or find one to join, go to the <%= linkStart %>Party Wanted (Looking for Group)<%= linkEnd %> Guild.",
|
||||
"create": "Create",
|
||||
"userId": "User ID",
|
||||
"invite": "Invite",
|
||||
|
||||
@@ -197,5 +197,11 @@
|
||||
"questTRexUndeadRageDescription": "This bar fills when you don't complete your Dailies. When it is full, the Skeletal Tyrannosaur will heal 30% of its remaining health!",
|
||||
"questTRexUndeadRageEffect": "`Skeletal Tyrannosaur uses SKELETON HEALING!`\n\nThe monster lets forth an unearthly roar, and some of its damaged bones knit back together!",
|
||||
|
||||
"questTRexDropTRexEgg": "Tyrannosaur (Egg)"
|
||||
"questTRexDropTRexEgg": "Tyrannosaur (Egg)",
|
||||
|
||||
"questRockText": "Escape the Cave Creature",
|
||||
"questRockNotes": "Crossing Habitica's Meandering Mountains with some friends, you make camp one night in a beautiful cave laced with shining minerals. But when you wake up the next morning, the entrance has disappeared, and the floor of the cave is shifting underneath you.<br><br>\"The mountain's alive!\" shouts your companion @pfeffernusse. \"These aren't crystals - these are teeth!\"<br><br>@Painter de Cluster grabs your hand. \"We'll have to find another way out – stay with me and don't get distracted, or we could be trapped in here forever!\"",
|
||||
"questRockBoss": "Crystal Colossus",
|
||||
"questRockCompletion": "Your diligence has allowed you to find a safe path through the living mountain. Standing in the sunshine, your friend @intune notices something glinting on the ground by the cave’s exit. You stoop to pick it up, and see that it’s a small rock with a vein of gold running through it. Beside it are a number of other rocks with rather peculiar shapes. They almost look like… eggs?",
|
||||
"questRockDropRockEgg": "Rock (Egg)"
|
||||
}
|
||||
|
||||
@@ -798,6 +798,7 @@ api.questEggs =
|
||||
Owl: text: t('questEggOwlText'), adjective: t('questEggOwlAdjective'), canBuy: false
|
||||
Penguin: text: t('questEggPenguinText'), adjective: t('questEggPenguinAdjective'), canBuy: false
|
||||
TRex: text: t('questEggTRexText'), adjective: t('questEggTRexAdjective'), canBuy: false
|
||||
Rock: text: t('questEggRockText'), adjective: t('questEggRockAdjective'), canBuy: false
|
||||
|
||||
_.each api.questEggs, (egg,key) ->
|
||||
_.defaults egg,
|
||||
@@ -1511,6 +1512,24 @@ api.quests =
|
||||
gp: 55
|
||||
exp: 500
|
||||
|
||||
rock:
|
||||
text: t('questRockText')
|
||||
notes: t('questRockNotes')
|
||||
completion: t('questRockCompletion')
|
||||
value: 4
|
||||
boss:
|
||||
name: t('questRockBoss')
|
||||
hp: 400
|
||||
str: 1.5
|
||||
drop:
|
||||
items: [
|
||||
{type: 'eggs', key: 'Rock', text: t('questRockDropRockEgg')}
|
||||
{type: 'eggs', key: 'Rock', text: t('questRockDropRockEgg')}
|
||||
{type: 'eggs', key: 'Rock', text: t('questRockDropRockEgg')}
|
||||
]
|
||||
gp: 31
|
||||
exp: 200
|
||||
|
||||
_.each api.quests, (v,key) ->
|
||||
_.defaults v, {key,canBuy:true}
|
||||
b = v.boss
|
||||
|
||||
@@ -40,7 +40,7 @@ echo Installing GraphicsMagick - provides gm and convert...
|
||||
apt-get install -qq graphicsmagick
|
||||
|
||||
echo Installing phantomjs and dependency...
|
||||
apt-get install -qq phantomjs libicu48
|
||||
apt-get install -qq libicu48
|
||||
|
||||
echo Installing requirements for grunt-spritesmith...
|
||||
apt-get install -qq pkg-config libcairo2-dev libjpeg-dev
|
||||
@@ -63,10 +63,10 @@ apt-get install -qq nodejs
|
||||
cd /vagrant
|
||||
|
||||
echo Installing grunt/bower...
|
||||
npm install -g grunt-cli bower
|
||||
npm install -g grunt-cli bower phantomjs
|
||||
|
||||
echo Installing HabitRPG
|
||||
npm install
|
||||
npm install --no-bin-link
|
||||
|
||||
echo Installing Bower packages
|
||||
sudo -H -u vagrant bower --config.interactive=false install -f
|
||||
|
||||
@@ -41,6 +41,8 @@ a.hint:hover
|
||||
.popover
|
||||
hr
|
||||
margin: 10px 0
|
||||
ul
|
||||
padding-left: 10px
|
||||
|
||||
@media (max-width: 768px)
|
||||
.container-fluid
|
||||
|
||||
@@ -84,14 +84,12 @@ angular.module('habitrpg')
|
||||
$scope._expandedMenu = ($scope._expandedMenu == menu) ? null : menu;
|
||||
};
|
||||
|
||||
function selectNotificationValue(mysteryValue, invitationValue, holidayCardValue, unallocatedValue, messageValue, noneValue) {
|
||||
function selectNotificationValue(mysteryValue, invitationValue, unallocatedValue, messageValue, noneValue) {
|
||||
var user = $scope.user;
|
||||
if (user.purchased && user.purchased.plan && user.purchased.plan.mysteryItems && user.purchased.plan.mysteryItems.length) {
|
||||
return mysteryValue;
|
||||
} else if ((user.invitations.party && user.invitations.party.id) || (user.invitations.guilds && user.invitations.guilds.length > 0)) {
|
||||
return invitationValue;
|
||||
} else if ((user.items.special.valentineReceived[0] || user.items.special.nyeReceived[0])) {
|
||||
return holidayCardValue;
|
||||
} else if (user.flags.classSelected && !(user.preferences && user.preferences.disableClasses) && user.stats.points) {
|
||||
return unallocatedValue;
|
||||
} else if (!(_.isEmpty(user.newMessages))) {
|
||||
@@ -105,14 +103,13 @@ angular.module('habitrpg')
|
||||
return selectNotificationValue(
|
||||
"glyphicon-gift",
|
||||
"glyphicon-user",
|
||||
"glyphicon-envelope",
|
||||
"glyphicon-plus-sign",
|
||||
"glyphicon-comment",
|
||||
"glyphicon-comment inactive");
|
||||
};
|
||||
|
||||
$scope.hasNoNotifications = function() {
|
||||
return selectNotificationValue(false, false, false, false, false, true);
|
||||
return selectNotificationValue(false, false, false, false, true);
|
||||
}
|
||||
|
||||
// ------ Social ----------
|
||||
|
||||
@@ -79,7 +79,7 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$
|
||||
$rootScope.playSound = function(id){
|
||||
if (!user.preferences.sound || user.preferences.sound == 'off') return;
|
||||
var theme = user.preferences.sound;
|
||||
var file = 'audio/' + theme + '/' + id;
|
||||
var file = 'common/audio/' + theme + '/' + id;
|
||||
document.getElementById('oggSource').src = file + '.ogg';
|
||||
document.getElementById('mp3Source').src = file + '.mp3';
|
||||
document.getElementById('sound').load();
|
||||
|
||||
@@ -183,7 +183,7 @@ script(type='text/ng-template', id='partials/options.inventory.drops.html')
|
||||
| {{::egg.value}}
|
||||
span.Pet_Currency_Gem1x.inline-gems
|
||||
//- buyable quest eggs
|
||||
each egg,quest in {gryphon:'Gryphon',hedgehog:'Hedgehog',ghost_stag:'Deer',rat:'Rat',octopus:'Octopus',dilatory_derby:'Seahorse',harpy:'Parrot',rooster:'Rooster',spider:'Spider',owl:'Owl',penguin:'Penguin'}
|
||||
each egg,quest in {gryphon:'Gryphon',hedgehog:'Hedgehog',ghost_stag:'Deer',rat:'Rat',octopus:'Octopus',dilatory_derby:'Seahorse',harpy:'Parrot',rooster:'Rooster',spider:'Spider',owl:'Owl',penguin:'Penguin',rock:'Rock'}
|
||||
div(ng-show='user.achievements.quests.#{quest} > 1')
|
||||
button.customize-option(popover='{{::Content.eggs.#{egg}.notes()}}', popover-title!=env.t("egg", {eggType: "{{::Content.eggs.#{egg}.text()}}"}), popover-trigger='mouseenter', popover-placement='top', popover-append-to-body='true', ng-click='purchase("eggs", Content.eggs.#{egg})', class='Pet_Egg_#{egg}')
|
||||
p
|
||||
@@ -244,7 +244,7 @@ script(type='text/ng-template', id='partials/options.inventory.drops.html')
|
||||
p
|
||||
| 20
|
||||
span.shop_gold
|
||||
div
|
||||
// div
|
||||
button.customize-option(popover='{{::Content.spells.special.valentine.notes()}}', popover-title='{{::Content.spells.special.valentine.text()}}', popover-trigger='mouseenter', popover-placement='right', popover-append-to-body='true', ng-click='castStart(Content.spells.special.valentine)', class='inventory_special_valentine')
|
||||
p
|
||||
| {{Content.spells.special.valentine.value}}
|
||||
|
||||
@@ -36,12 +36,7 @@ script(type='text/ng-template', id='partials/options.social.party.html')
|
||||
=env.t('noPartyText')
|
||||
pre.prettyprint.
|
||||
{{user.id}}
|
||||
p
|
||||
=env.t('startLFG')
|
||||
|
|
||||
a(href="http://habitrpg.wikia.com/wiki/Board:The_Archery_-_LFG")=env.t('linkLFG')
|
||||
|
|
||||
=env.t('endLFG')
|
||||
p!=env.t('LFG', {linkStart: "<a href='https://habitrpg.com/#/options/groups/guilds/f2db2a7f-13c5-454d-b3ee-ea1f5089e601'>", linkEnd: "</a>"})
|
||||
include ./create-group
|
||||
|
||||
script(type='text/ng-template', id='partials/options.social.guilds.public.html')
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
a(target='_blank', href='/static/community-guidelines')=env.t('communityGuidelines')
|
||||
tr
|
||||
td
|
||||
a(target='_blank', href='http://habitrpg.wikia.com/wiki/Board:The_Archery_-_LFG')=env.t('lfgPosts')
|
||||
a(href='https://habitrpg.com/#/options/groups/guilds/f2db2a7f-13c5-454d-b3ee-ea1f5089e601')=env.t('lfgPosts')
|
||||
tr
|
||||
td
|
||||
a(target='_blank', href='https://vimeo.com/57654086')=env.t('tutorial')
|
||||
|
||||
@@ -202,7 +202,7 @@ nav.toolbar(ng-controller='AuthCtrl', ng-class='{active: isToolbarHidden}')
|
||||
span {{v.name}}
|
||||
a(ng-click='Groups.seenMessage(k)', popover=env.t('clear'),popover-placement='right',popover-trigger='mouseenter',popover-append-to-body='true')
|
||||
span.glyphicon.glyphicon-remove-circle
|
||||
li(ng-if='user.items.special.valentineReceived[0] || user.items.special.nyeReceived[0]')
|
||||
// li(ng-if='user.items.special.valentineReceived[0] || user.items.special.nyeReceived[0]')
|
||||
a(ng-click='$state.go("options.inventory.drops"); expandMenu(null)')
|
||||
span.glyphicon.glyphicon-envelope
|
||||
span=env.t('holidayCard')
|
||||
|
||||
@@ -1,21 +1,38 @@
|
||||
h5 2/12/2015 - VALENTINE'S DAY AND NEW HAIRSTYLES!
|
||||
h5 2/17/2015 - NEW PET QUEST AND COMMUNITY GUIDELINE UPDATES
|
||||
hr
|
||||
tr
|
||||
td
|
||||
h5 Happy Valentine's Day!
|
||||
p Help motivate all of the lovely people in your life by sending them a caring valentine. Valentines can be purchased for 10 gold from the Market. For spreading love and joy throughout the community, both the giver AND the receiver get a coveted "adoring friends" badge. Hooray!
|
||||
p.small.muted by Lemoness and SabreCat
|
||||
.quest_rock.pull-right
|
||||
h5 New Pet Quest: Rocks!
|
||||
p It seemed like a simple hike... until we discovered that the cave was alive! You can get the newest Pet Quest, "Escape the Cave Creature," in the <a href='https://habitrpg.com/#/options/inventory/drops' target='_blank'>Market</a>. If you defeat it, you'll get some cuddly pet rocks!
|
||||
p.small.muted by itokro, Pfeffernusse, Painter de Cluster, and intone
|
||||
tr
|
||||
td
|
||||
h5 New Hairstyles!
|
||||
.promo_updos.pull-right
|
||||
p There are a new set of updo hairstyles available in the <a href='https://habitrpg.com/#/options/profile/avatar' target='_blank'>Avatar Customization page</a>! Have fun customizing your characters.
|
||||
p.small.muted by Crystalphoenix, Mariahm, Painter de Cluster, Leephon, Beffymaroo, Sungabraverday, Lemoness, and Bailey
|
||||
h5 Community Guideline Updates
|
||||
p We've updated the <a href='https://habitrpg.com/static/community-guidelines' target='_blank'>Community Guidelines</a> to include the following:
|
||||
ul
|
||||
li Blade, a new mod, is listed!
|
||||
li Private Messages have been added to the guidelines for Private Spaces.
|
||||
li Spamming is now expressly forbidden.
|
||||
li Sexism has been added to the list of unacceptable behaviors.
|
||||
li Making duplicate accounts to circumvent consequences is now expressly forbidden.
|
||||
|
||||
hr
|
||||
a(href='/static/old-news', target='_blank') Read older news
|
||||
|
||||
mixin oldNews
|
||||
h5 2/12/2015
|
||||
tr
|
||||
td
|
||||
h5 Happy Valentine's Day!
|
||||
p Help motivate all of the lovely people in your life by sending them a caring valentine. Valentines can be purchased for 10 gold from the Market. For spreading love and joy throughout the community, both the giver AND the receiver get a coveted "adoring friends" badge. Hooray!
|
||||
p.small.muted by Lemoness and SabreCat
|
||||
tr
|
||||
td
|
||||
h5 New Hairstyles!
|
||||
.promo_updos.pull-right
|
||||
p There are a new set of updo hairstyles available in the <a href='https://habitrpg.com/#/options/profile/avatar' target='_blank'>Avatar Customization page</a>! Have fun customizing your characters.
|
||||
p.small.muted by Crystalphoenix, Mariahm, Painter de Cluster, Leephon, Beffymaroo, Sungabraverday, Lemoness, and Bailey
|
||||
h5 2/8/2015
|
||||
tr
|
||||
td
|
||||
|
||||
@@ -46,6 +46,7 @@ div(ng-if='profile.achievements.streak || user._id == profile._id')
|
||||
.achievement.achievement-thermometer(ng-show='profile.achievements.streak')
|
||||
div(ng-class='{muted: !profile.achievements.streak}')
|
||||
h5(ng-show='profile.achievements.streak > 1 || !profile.achievements.streak')
|
||||
|
||||
| {{profile.achievements.streak || 0 }}
|
||||
=env.t('streakName')
|
||||
small(ng-show='profile.achievements.streak > 1 || !profile.achievements.streak')=env.t('streakText', {streaks: "{{profile.achievements.streak || 0 }}"})
|
||||
@@ -53,10 +54,12 @@ div(ng-if='profile.achievements.streak || user._id == profile._id')
|
||||
=env.t('streakSingular')
|
||||
small(ng-show='profile.achievements.streak == 1')=env.t('streakSingularText')
|
||||
hr
|
||||
|
||||
div(ng-if='profile.achievements.perfect || user._id == profile._id')
|
||||
.achievement.achievement-perfect(ng-show='profile.achievements.perfect')
|
||||
div(ng-class='{muted: !profile.achievements.perfect}')
|
||||
h5(ng-show='profile.achievements.perfect > 1 || !profile.achievements.perfect')
|
||||
|
||||
| {{profile.achievements.perfect || 0 }}
|
||||
=env.t('perfectName')
|
||||
small(ng-show='profile.achievements.perfect > 1 || !profile.achievements.perfect')=env.t('perfectText', {perfects: "{{profile.achievements.perfect || 0 }}"})
|
||||
@@ -101,6 +104,7 @@ div(ng-if='profile.achievements.triadBingo || user._id == profile._id')
|
||||
.achievement.achievement-triadbingo(ng-show='profile.achievements.triadBingo')
|
||||
div(ng-class='{muted: !profile.achievements.triadBingo}')
|
||||
h5=env.t('triadBingoName')
|
||||
|
||||
small=env.t('triadBingoText')
|
||||
small(ng-if='profile.achievements.triadBingoCount')
|
||||
=env.t('triadBingoText2', {count: "{{profile.achievements.triadBingoCount}}"})
|
||||
@@ -125,16 +129,20 @@ div(ng-if='::profile.achievements.helpedHabit')
|
||||
+aLink('http://community.habitrpg.com/node/290', env.t('helpedText2'))
|
||||
hr
|
||||
|
||||
div(ng-if=':: profile.achievements.originalUser || profile.achievements.veteran')
|
||||
div(ng-if=':: profile.achievements.veteran')
|
||||
.achievement.achievement-cake
|
||||
div(ng-if='::profile.achievements.veteran')
|
||||
h5=env.t('veteran')
|
||||
small=env.t('veteranText')
|
||||
hr
|
||||
|
||||
div(ng-if=':: profile.achievements.originalUser')
|
||||
.achievement.achievement-alpha
|
||||
div(ng-if='::profile.achievements.originalUser')
|
||||
h5=env.t('originalUser')
|
||||
small!=env.t('originalUserText')
|
||||
hr
|
||||
|
||||
|
||||
div(ng-if='profile.achievements.challenges || user._id == profile._id')
|
||||
// This is a very strange icon to use. revisit
|
||||
.achievement.achievement-karaoke(ng-show='profile.achievements.challenges')
|
||||
@@ -173,6 +181,7 @@ div(ng-if='::profile.achievements.habitBirthdays')
|
||||
.achievement.achievement-habitBirthday
|
||||
h5=env.t('habitBirthday')
|
||||
small(ng-if='::profile.achievements.habitBirthdays == 1')
|
||||
|
||||
=env.t('habitBirthdayText')
|
||||
small(ng-if='::profile.achievements.habitBirthdays > 1')
|
||||
=env.t('habitBirthdayPluralText', {number: "{{profile.achievements.habitBirthdays}}"})
|
||||
|
||||