Merged in feat/lokalise-sync-260105 (pull request #3387)

feat(lokalise): fixed correct message and sync

* feat(lokalise): fixed correct message and sync
This commit is contained in:
Linus Flood
2026-01-05 11:33:08 +00:00
parent d23137a69d
commit 3d62c16899
4 changed files with 937 additions and 221 deletions

View File

@@ -33,6 +33,12 @@
"value": "Reserve with card" "value": "Reserve with card"
} }
], ],
"addAncillary.deliveryDetailsStep.commentLabel": [
{
"type": 0,
"value": "Is there anything else you would like us to know before your arrival?"
}
],
"addAncillary.deliveryDetailsStep.deliveryTimeDescription": [ "addAncillary.deliveryDetailsStep.deliveryTimeDescription": [
{ {
"type": 0, "type": 0,
@@ -42,7 +48,7 @@
"addAncillary.deliveryDetailsStep.optionalTextLabel": [ "addAncillary.deliveryDetailsStep.optionalTextLabel": [
{ {
"type": 0, "type": 0,
"value": "Other Requests" "value": "Other requests"
} }
], ],
"addAncillary.selectQuantityStep.breakfastInfoMessage": [ "addAncillary.selectQuantityStep.breakfastInfoMessage": [
@@ -51,6 +57,20 @@
"value": "Breakfast can only be added for the entire duration of the stay and for all guests." "value": "Breakfast can only be added for the entire duration of the stay and for all guests."
} }
], ],
"addAncillary.selectQuantityStep.costPerUnit": [
{
"type": 1,
"value": "cost"
},
{
"type": 0,
"value": "/per "
},
{
"type": 1,
"value": "unit"
}
],
"addAncillary.selectQuantityStep.insufficientPoints": [ "addAncillary.selectQuantityStep.insufficientPoints": [
{ {
"type": 0, "type": 0,
@@ -81,6 +101,18 @@
"value": "Select quantity" "value": "Select quantity"
} }
], ],
"addAncillary.selectQuantityStep.selectQuantityTitle": [
{
"type": 0,
"value": "How would you like to pay?"
}
],
"addAncillaryFlowModal.addToBooking": [
{
"type": 0,
"value": "Add to booking"
}
],
"addAncillaryFlowModal.ancillaryAdded": [ "addAncillaryFlowModal.ancillaryAdded": [
{ {
"type": 1, "type": 1,
@@ -111,6 +143,16 @@
"value": "Free for kids (under 4)" "value": "Free for kids (under 4)"
} }
], ],
"addAncillaryFlowModal.perUnit": [
{
"type": 0,
"value": "/per "
},
{
"type": 1,
"value": "unit"
}
],
"addAncillaryFlowModal.pricePerNightPerAdult": [ "addAncillaryFlowModal.pricePerNightPerAdult": [
{ {
"type": 1, "type": 1,
@@ -131,6 +173,12 @@
"value": "/night for kids (ages 412)" "value": "/night for kids (ages 412)"
} }
], ],
"addAncillaryFlowModal.proceedToDelivery": [
{
"type": 0,
"value": "Proceed to delivery"
}
],
"alternativeHotels.title": [ "alternativeHotels.title": [
{ {
"type": 0, "type": 0,
@@ -147,6 +195,42 @@
"value": "Delivered at:" "value": "Delivered at:"
} }
], ],
"ancillaries.deliveryDetailsStep.changeTime.cta": [
{
"type": 0,
"value": "Change time"
}
],
"ancillaries.deliveryDetailsStep.itemTitle": [
{
"type": 0,
"value": "Your item"
}
],
"ancillaries.deliveryDetailsStep.select.errorMessage": [
{
"type": 0,
"value": "Select a time for when you want your extras to be delivered."
}
],
"ancillaries.deliveryDetailsStep.select.title": [
{
"type": 0,
"value": "Select time of delivery"
}
],
"ancillaries.deliveryDetailsStep.specialRequests.cta": [
{
"type": 0,
"value": "Add special request"
}
],
"ancillaries.deliveryDetailsStep.specialRequests.title": [
{
"type": 0,
"value": "Special requests (optional)"
}
],
"ancillaries.unableToDisplayBreakfastPrices": [ "ancillaries.unableToDisplayBreakfastPrices": [
{ {
"type": 0, "type": 0,
@@ -878,7 +962,7 @@
"bookingWidget.bookingCode.tooltip": [ "bookingWidget.bookingCode.tooltip": [
{ {
"type": 0, "type": 0,
"value": "If you're booking a promotional offer or a Corporate negotiated rate you'll need a special booking code. Don't use any special characters such as (.) (,) (-) (:). If you would like to make a booking with code VOF, please call us +46 8 517 517 20.Save your booking code for the next time you visit the page by ticking the box “Remember”. Don't tick the box if you're using a public computer to avoid unauthorized access to your booking code." "value": "If you're booking a promotional offer or a Corporate negotiated rate you'll need a special booking code. Don't use any special characters such as (.) (,) (-) (:). If you would like to make a booking with code VOF, please call us +46 8 517 517 20. Save your booking code for the next time you visit the page by ticking the box “Remember”. Don't tick the box if you're using a public computer to avoid unauthorized access to your booking code."
} }
], ],
"bookingWidget.button.search": [ "bookingWidget.button.search": [
@@ -1073,6 +1157,12 @@
"value": "A destination or hotel name is needed to be able to search for a hotel room." "value": "A destination or hotel name is needed to be able to search for a hotel room."
} }
], ],
"bookingwidget.dropdown.children": [
{
"type": 0,
"value": "Children (012 years)"
}
],
"breadcrumbs.label": [ "breadcrumbs.label": [
{ {
"type": 0, "type": 0,
@@ -1995,6 +2085,42 @@
"value": "or" "value": "or"
} }
], ],
"common.orNumberOfPoints": [
{
"type": 0,
"value": "or "
},
{
"offset": 0,
"options": {
"one": {
"value": [
{
"type": 7
},
{
"type": 0,
"value": " point"
}
]
},
"other": {
"value": [
{
"type": 7
},
{
"type": 0,
"value": " points"
}
]
}
},
"pluralType": "cardinal",
"type": 6,
"value": "points"
}
],
"common.other": [ "common.other": [
{ {
"type": 0, "type": 0,
@@ -2025,12 +2151,24 @@
"value": "Password" "value": "Password"
} }
], ],
"common.paymentCard": [
{
"type": 0,
"value": "Payment card"
}
],
"common.perAdultNight": [ "common.perAdultNight": [
{ {
"type": 0, "type": 0,
"value": "Per adult/night" "value": "Per adult/night"
} }
], ],
"common.person": [
{
"type": 0,
"value": "person"
}
],
"common.petFriendly": [ "common.petFriendly": [
{ {
"type": 0, "type": 0,
@@ -2116,6 +2254,12 @@
"value": "Remove" "value": "Remove"
} }
], ],
"common.reviewAndConfirm": [
{
"type": 0,
"value": "Review & Confirm"
}
],
"common.room": [ "common.room": [
{ {
"type": 0, "type": 0,
@@ -4298,12 +4442,24 @@
"value": "count" "value": "count"
} }
], ],
"lightbox.nextImage": [
{
"type": 0,
"value": "Next image"
}
],
"lightbox.openImage": [ "lightbox.openImage": [
{ {
"type": 0, "type": 0,
"value": "Open image" "value": "Open image"
} }
], ],
"lightbox.previousImage": [
{
"type": 0,
"value": "Previous image"
}
],
"linkEmploymentError.alreadyLinkedHeading": [ "linkEmploymentError.alreadyLinkedHeading": [
{ {
"type": 0, "type": 0,
@@ -5623,6 +5779,30 @@
"value": "My membership cards" "value": "My membership cards"
} }
], ],
"myPages.myStay.ancillaries.insufficientPointsMessage": [
{
"type": 0,
"value": "You don't have enough points for this item"
}
],
"myPages.myStay.ancillaries.reachedMaxPointsMessage": [
{
"type": 0,
"value": "You have reached your points limit."
}
],
"myPages.myStay.ancillaries.reachedMaxPointsStepperMessage": [
{
"type": 0,
"value": "Youve reached your points limit and cant add more items with points."
}
],
"myPages.myStay.ancillaries.spendablePointsTitle": [
{
"type": 0,
"value": "Your spendable points"
}
],
"myPages.nameWithCardMembershipType": [ "myPages.nameWithCardMembershipType": [
{ {
"type": 0, "type": 0,
@@ -6609,6 +6789,82 @@
"value": " days" "value": " days"
} }
], ],
"nextStay.inXMonths": [
{
"type": 0,
"value": "In "
},
{
"offset": 0,
"options": {
"one": {
"value": [
{
"type": 7
},
{
"type": 0,
"value": " month"
}
]
},
"other": {
"value": [
{
"type": 0,
"value": " "
},
{
"type": 7
},
{
"type": 0,
"value": " months"
}
]
}
},
"pluralType": "cardinal",
"type": 6,
"value": "months"
}
],
"nextStay.inXYears": [
{
"type": 0,
"value": "In "
},
{
"offset": 0,
"options": {
"one": {
"value": [
{
"type": 7
},
{
"type": 0,
"value": " year"
}
]
},
"other": {
"value": [
{
"type": 7
},
{
"type": 0,
"value": " years"
}
]
}
},
"pluralType": "cardinal",
"type": 6,
"value": "years"
}
],
"nextStay.myStayAt": [ "nextStay.myStayAt": [
{ {
"type": 0, "type": 0,
@@ -7240,6 +7496,50 @@
"value": "expiryDate" "value": "expiryDate"
} }
], ],
"previousStays.nightsStayed": [
{
"type": 0,
"value": "Nights stayed"
}
],
"previousStays.progressAriaLabel": [
{
"type": 1,
"value": "nightsStayed"
},
{
"type": 0,
"value": " of "
},
{
"type": 1,
"value": "maxNights"
},
{
"type": 0,
"value": " nights stayed towards "
},
{
"type": 1,
"value": "levelName"
}
],
"previousStays.sinceDate": [
{
"type": 0,
"value": "Since "
},
{
"type": 1,
"value": "date"
}
],
"previousStays.totalStays": [
{
"type": 0,
"value": "Total stays"
}
],
"price.numberOfVouchers": [ "price.numberOfVouchers": [
{ {
"offset": 0, "offset": 0,
@@ -7904,6 +8204,30 @@
"value": "Menus" "value": "Menus"
} }
], ],
"rewardNights.offerPrice": [
{
"type": 0,
"value": "Offer price"
}
],
"rewardNights.stayBetween:": [
{
"type": 0,
"value": "Stay between:"
}
],
"rewardNights.table.destination": [
{
"type": 0,
"value": "Destination"
}
],
"rewardNights.table.hotel": [
{
"type": 0,
"value": "Hotel"
}
],
"rewards.active": [ "rewards.active": [
{ {
"type": 0, "type": 0,
@@ -8423,7 +8747,7 @@
}, },
{ {
"type": 0, "type": 0,
"value": "-" "value": ""
}, },
{ {
"type": 1, "type": 1,
@@ -8899,6 +9223,18 @@
"value": "You have no upcoming stays at the moment" "value": "You have no upcoming stays at the moment"
} }
], ],
"stays.previous.title": [
{
"type": 0,
"value": "Previous stays"
}
],
"stays.upcoming.title": [
{
"type": 0,
"value": "Upcoming stays"
}
],
"stays.whereToGoNext": [ "stays.whereToGoNext": [
{ {
"type": 0, "type": 0,
@@ -9042,6 +9378,30 @@
"value": "User not found" "value": "User not found"
} }
], ],
"videoPlayer.mute": [
{
"type": 0,
"value": "Mute video"
}
],
"videoPlayer.pause": [
{
"type": 0,
"value": "Pause video"
}
],
"videoPlayer.play": [
{
"type": 0,
"value": "Play video"
}
],
"videoPlayer.unmute": [
{
"type": 0,
"value": "Unmute video"
}
],
"webview.genericUserError": [ "webview.genericUserError": [
{ {
"type": 0, "type": 0,

View File

@@ -150,7 +150,8 @@ function InnerSelectQuantityStep({
cost: intl.formatMessage( cost: intl.formatMessage(
{ {
id: "common.numberOfPoints", id: "common.numberOfPoints",
defaultMessage: "{points} points", defaultMessage:
"{points, plural, one {# point} other {# points}}",
}, },
{ points: pointsCost } { points: pointsCost }
), ),
@@ -162,7 +163,8 @@ function InnerSelectQuantityStep({
totalCostMessage={intl.formatMessage( totalCostMessage={intl.formatMessage(
{ {
id: "common.numberOfPoints", id: "common.numberOfPoints",
defaultMessage: "{points} points", defaultMessage:
"{points, plural, one {# point} other {# points}}",
}, },
{ points: pointsCost * quantity } { points: pointsCost * quantity }
)} )}

View File

@@ -33,6 +33,12 @@
"value": "Reserve with card" "value": "Reserve with card"
} }
], ],
"addAncillary.deliveryDetailsStep.commentLabel": [
{
"type": 0,
"value": "Is there anything else you would like us to know before your arrival?"
}
],
"addAncillary.deliveryDetailsStep.deliveryTimeDescription": [ "addAncillary.deliveryDetailsStep.deliveryTimeDescription": [
{ {
"type": 0, "type": 0,
@@ -42,7 +48,7 @@
"addAncillary.deliveryDetailsStep.optionalTextLabel": [ "addAncillary.deliveryDetailsStep.optionalTextLabel": [
{ {
"type": 0, "type": 0,
"value": "Other Requests" "value": "Other requests"
} }
], ],
"addAncillary.selectQuantityStep.breakfastInfoMessage": [ "addAncillary.selectQuantityStep.breakfastInfoMessage": [
@@ -51,6 +57,20 @@
"value": "Breakfast can only be added for the entire duration of the stay and for all guests." "value": "Breakfast can only be added for the entire duration of the stay and for all guests."
} }
], ],
"addAncillary.selectQuantityStep.costPerUnit": [
{
"type": 1,
"value": "cost"
},
{
"type": 0,
"value": "/per "
},
{
"type": 1,
"value": "unit"
}
],
"addAncillary.selectQuantityStep.insufficientPoints": [ "addAncillary.selectQuantityStep.insufficientPoints": [
{ {
"type": 0, "type": 0,
@@ -81,6 +101,18 @@
"value": "Select quantity" "value": "Select quantity"
} }
], ],
"addAncillary.selectQuantityStep.selectQuantityTitle": [
{
"type": 0,
"value": "How would you like to pay?"
}
],
"addAncillaryFlowModal.addToBooking": [
{
"type": 0,
"value": "Add to booking"
}
],
"addAncillaryFlowModal.ancillaryAdded": [ "addAncillaryFlowModal.ancillaryAdded": [
{ {
"type": 1, "type": 1,
@@ -111,6 +143,16 @@
"value": "Free for kids (under 4)" "value": "Free for kids (under 4)"
} }
], ],
"addAncillaryFlowModal.perUnit": [
{
"type": 0,
"value": "/per "
},
{
"type": 1,
"value": "unit"
}
],
"addAncillaryFlowModal.pricePerNightPerAdult": [ "addAncillaryFlowModal.pricePerNightPerAdult": [
{ {
"type": 1, "type": 1,
@@ -131,6 +173,12 @@
"value": "/night for kids (ages 412)" "value": "/night for kids (ages 412)"
} }
], ],
"addAncillaryFlowModal.proceedToDelivery": [
{
"type": 0,
"value": "Proceed to delivery"
}
],
"alternativeHotels.title": [ "alternativeHotels.title": [
{ {
"type": 0, "type": 0,
@@ -147,6 +195,42 @@
"value": "Delivered at:" "value": "Delivered at:"
} }
], ],
"ancillaries.deliveryDetailsStep.changeTime.cta": [
{
"type": 0,
"value": "Change time"
}
],
"ancillaries.deliveryDetailsStep.itemTitle": [
{
"type": 0,
"value": "Your item"
}
],
"ancillaries.deliveryDetailsStep.select.errorMessage": [
{
"type": 0,
"value": "Select a time for when you want your extras to be delivered."
}
],
"ancillaries.deliveryDetailsStep.select.title": [
{
"type": 0,
"value": "Select time of delivery"
}
],
"ancillaries.deliveryDetailsStep.specialRequests.cta": [
{
"type": 0,
"value": "Add special request"
}
],
"ancillaries.deliveryDetailsStep.specialRequests.title": [
{
"type": 0,
"value": "Special requests (optional)"
}
],
"ancillaries.unableToDisplayBreakfastPrices": [ "ancillaries.unableToDisplayBreakfastPrices": [
{ {
"type": 0, "type": 0,
@@ -878,7 +962,7 @@
"bookingWidget.bookingCode.tooltip": [ "bookingWidget.bookingCode.tooltip": [
{ {
"type": 0, "type": 0,
"value": "If you're booking a promotional offer or a Corporate negotiated rate you'll need a special booking code. Don't use any special characters such as (.) (,) (-) (:). If you would like to make a booking with code VOF, please call us +46 8 517 517 20.Save your booking code for the next time you visit the page by ticking the box “Remember”. Don't tick the box if you're using a public computer to avoid unauthorized access to your booking code." "value": "If you're booking a promotional offer or a Corporate negotiated rate you'll need a special booking code. Don't use any special characters such as (.) (,) (-) (:). If you would like to make a booking with code VOF, please call us +46 8 517 517 20. Save your booking code for the next time you visit the page by ticking the box “Remember”. Don't tick the box if you're using a public computer to avoid unauthorized access to your booking code."
} }
], ],
"bookingWidget.button.search": [ "bookingWidget.button.search": [
@@ -1073,6 +1157,12 @@
"value": "A destination or hotel name is needed to be able to search for a hotel room." "value": "A destination or hotel name is needed to be able to search for a hotel room."
} }
], ],
"bookingwidget.dropdown.children": [
{
"type": 0,
"value": "Children (012 years)"
}
],
"breadcrumbs.label": [ "breadcrumbs.label": [
{ {
"type": 0, "type": 0,
@@ -1995,6 +2085,42 @@
"value": "or" "value": "or"
} }
], ],
"common.orNumberOfPoints": [
{
"type": 0,
"value": "or "
},
{
"offset": 0,
"options": {
"one": {
"value": [
{
"type": 7
},
{
"type": 0,
"value": " point"
}
]
},
"other": {
"value": [
{
"type": 7
},
{
"type": 0,
"value": " points"
}
]
}
},
"pluralType": "cardinal",
"type": 6,
"value": "points"
}
],
"common.other": [ "common.other": [
{ {
"type": 0, "type": 0,
@@ -2025,12 +2151,24 @@
"value": "Password" "value": "Password"
} }
], ],
"common.paymentCard": [
{
"type": 0,
"value": "Payment card"
}
],
"common.perAdultNight": [ "common.perAdultNight": [
{ {
"type": 0, "type": 0,
"value": "Per adult/night" "value": "Per adult/night"
} }
], ],
"common.person": [
{
"type": 0,
"value": "person"
}
],
"common.petFriendly": [ "common.petFriendly": [
{ {
"type": 0, "type": 0,
@@ -2116,6 +2254,12 @@
"value": "Remove" "value": "Remove"
} }
], ],
"common.reviewAndConfirm": [
{
"type": 0,
"value": "Review & Confirm"
}
],
"common.room": [ "common.room": [
{ {
"type": 0, "type": 0,
@@ -4298,12 +4442,24 @@
"value": "count" "value": "count"
} }
], ],
"lightbox.nextImage": [
{
"type": 0,
"value": "Next image"
}
],
"lightbox.openImage": [ "lightbox.openImage": [
{ {
"type": 0, "type": 0,
"value": "Open image" "value": "Open image"
} }
], ],
"lightbox.previousImage": [
{
"type": 0,
"value": "Previous image"
}
],
"linkEmploymentError.alreadyLinkedHeading": [ "linkEmploymentError.alreadyLinkedHeading": [
{ {
"type": 0, "type": 0,
@@ -5623,6 +5779,30 @@
"value": "My membership cards" "value": "My membership cards"
} }
], ],
"myPages.myStay.ancillaries.insufficientPointsMessage": [
{
"type": 0,
"value": "You don't have enough points for this item"
}
],
"myPages.myStay.ancillaries.reachedMaxPointsMessage": [
{
"type": 0,
"value": "You have reached your points limit."
}
],
"myPages.myStay.ancillaries.reachedMaxPointsStepperMessage": [
{
"type": 0,
"value": "Youve reached your points limit and cant add more items with points."
}
],
"myPages.myStay.ancillaries.spendablePointsTitle": [
{
"type": 0,
"value": "Your spendable points"
}
],
"myPages.nameWithCardMembershipType": [ "myPages.nameWithCardMembershipType": [
{ {
"type": 0, "type": 0,
@@ -6609,6 +6789,82 @@
"value": " days" "value": " days"
} }
], ],
"nextStay.inXMonths": [
{
"type": 0,
"value": "In "
},
{
"offset": 0,
"options": {
"one": {
"value": [
{
"type": 7
},
{
"type": 0,
"value": " month"
}
]
},
"other": {
"value": [
{
"type": 0,
"value": " "
},
{
"type": 7
},
{
"type": 0,
"value": " months"
}
]
}
},
"pluralType": "cardinal",
"type": 6,
"value": "months"
}
],
"nextStay.inXYears": [
{
"type": 0,
"value": "In "
},
{
"offset": 0,
"options": {
"one": {
"value": [
{
"type": 7
},
{
"type": 0,
"value": " year"
}
]
},
"other": {
"value": [
{
"type": 7
},
{
"type": 0,
"value": " years"
}
]
}
},
"pluralType": "cardinal",
"type": 6,
"value": "years"
}
],
"nextStay.myStayAt": [ "nextStay.myStayAt": [
{ {
"type": 0, "type": 0,
@@ -7240,6 +7496,50 @@
"value": "expiryDate" "value": "expiryDate"
} }
], ],
"previousStays.nightsStayed": [
{
"type": 0,
"value": "Nights stayed"
}
],
"previousStays.progressAriaLabel": [
{
"type": 1,
"value": "nightsStayed"
},
{
"type": 0,
"value": " of "
},
{
"type": 1,
"value": "maxNights"
},
{
"type": 0,
"value": " nights stayed towards "
},
{
"type": 1,
"value": "levelName"
}
],
"previousStays.sinceDate": [
{
"type": 0,
"value": "Since "
},
{
"type": 1,
"value": "date"
}
],
"previousStays.totalStays": [
{
"type": 0,
"value": "Total stays"
}
],
"price.numberOfVouchers": [ "price.numberOfVouchers": [
{ {
"offset": 0, "offset": 0,
@@ -7904,6 +8204,30 @@
"value": "Menus" "value": "Menus"
} }
], ],
"rewardNights.offerPrice": [
{
"type": 0,
"value": "Offer price"
}
],
"rewardNights.stayBetween:": [
{
"type": 0,
"value": "Stay between:"
}
],
"rewardNights.table.destination": [
{
"type": 0,
"value": "Destination"
}
],
"rewardNights.table.hotel": [
{
"type": 0,
"value": "Hotel"
}
],
"rewards.active": [ "rewards.active": [
{ {
"type": 0, "type": 0,
@@ -8423,7 +8747,7 @@
}, },
{ {
"type": 0, "type": 0,
"value": "-" "value": ""
}, },
{ {
"type": 1, "type": 1,
@@ -8899,6 +9223,18 @@
"value": "You have no upcoming stays at the moment" "value": "You have no upcoming stays at the moment"
} }
], ],
"stays.previous.title": [
{
"type": 0,
"value": "Previous stays"
}
],
"stays.upcoming.title": [
{
"type": 0,
"value": "Upcoming stays"
}
],
"stays.whereToGoNext": [ "stays.whereToGoNext": [
{ {
"type": 0, "type": 0,
@@ -9042,6 +9378,30 @@
"value": "User not found" "value": "User not found"
} }
], ],
"videoPlayer.mute": [
{
"type": 0,
"value": "Mute video"
}
],
"videoPlayer.pause": [
{
"type": 0,
"value": "Pause video"
}
],
"videoPlayer.play": [
{
"type": 0,
"value": "Play video"
}
],
"videoPlayer.unmute": [
{
"type": 0,
"value": "Unmute video"
}
],
"webview.genericUserError": [ "webview.genericUserError": [
{ {
"type": 0, "type": 0,

View File

@@ -15,265 +15,259 @@ const lokaliseLogger = createLogger("lokalise");
let resolvePerf: (value?: unknown) => void; let resolvePerf: (value?: unknown) => void;
const performanceMetrics = new Promise((resolve) => { const performanceMetrics = new Promise((resolve) => {
resolvePerf = resolve; resolvePerf = resolve;
}); });
const perf = new PerformanceObserver((items) => { const perf = new PerformanceObserver((items) => {
const entries = items.getEntries(); const entries = items.getEntries();
for (const entry of entries) { for (const entry of entries) {
if (entry.name === "done") { if (entry.name === "done") {
// This is the last measure meant for clean up // This is the last measure meant for clean up
performance.clearMarks(); performance.clearMarks();
perf.disconnect(); perf.disconnect();
if (typeof resolvePerf === "function") { if (typeof resolvePerf === "function") {
resolvePerf(); resolvePerf();
} }
} else { } else {
lokaliseLogger.info( lokaliseLogger.info(
`[metrics] ${entry.name} completed in ${entry.duration} ms` `[metrics] ${entry.name} completed in ${entry.duration} ms`
); );
}
} }
performance.clearMeasures(); }
performance.clearMeasures();
}); });
async function waitUntilUploadDone(processId: string) { async function waitUntilUploadDone(processId: string) {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
const interval = setInterval(async () => { const interval = setInterval(async () => {
try { try {
performance.mark("waitUntilUploadDoneStart"); performance.mark("waitUntilUploadDoneStart");
lokaliseLogger.debug("Checking upload status..."); lokaliseLogger.debug("Checking upload status...");
performance.mark("getProcessStart"); performance.mark("getProcessStart");
const process = await lokaliseApi const process = await lokaliseApi.queuedProcesses().get(processId, {
.queuedProcesses() project_id: projectId,
.get(processId, { });
project_id: projectId, performance.mark("getProcessEnd");
}); performance.measure(
performance.mark("getProcessEnd"); "Get Queued Process",
performance.measure( "getProcessStart",
"Get Queued Process", "getProcessEnd"
"getProcessStart", );
"getProcessEnd"
);
lokaliseLogger.debug(`Status: ${process.status}`); lokaliseLogger.debug(`Status: ${process.status}`);
if (process.status === "finished") { if (process.status === "finished") {
clearInterval(interval); clearInterval(interval);
performance.mark("waitUntilUploadDoneEnd", { performance.mark("waitUntilUploadDoneEnd", {
detail: "success", detail: "success",
}); });
performance.measure( performance.measure(
"Wait on upload", "Wait on upload",
"waitUntilUploadDoneStart", "waitUntilUploadDoneStart",
"waitUntilUploadDoneEnd" "waitUntilUploadDoneEnd"
); );
resolve(); resolve();
} else if (process.status === "failed") { } else if (process.status === "failed") {
throw process; throw process;
} }
} catch (e) { } catch (e) {
clearInterval(interval); clearInterval(interval);
lokaliseLogger.error("An error occurred:", e); lokaliseLogger.error("An error occurred:", e);
performance.mark("waitUntilUploadDoneEnd", { detail: e }); performance.mark("waitUntilUploadDoneEnd", { detail: e });
performance.measure( performance.measure(
"Wait on upload", "Wait on upload",
"waitUntilUploadDoneStart", "waitUntilUploadDoneStart",
"waitUntilUploadDoneEnd" "waitUntilUploadDoneEnd"
); );
reject(); reject();
} }
}, 1000); }, 1000);
}); });
} }
export async function upload(filepath: string) { export async function upload(filepath: string) {
perf.observe({ type: "measure" }); perf.observe({ type: "measure" });
try { try {
lokaliseLogger.debug(`Uploading ${filepath}...`); lokaliseLogger.debug(`Uploading ${filepath}...`);
performance.mark("uploadStart"); performance.mark("uploadStart");
performance.mark("sourceFileReadStart"); performance.mark("sourceFileReadStart");
const data = await fs.readFile(filepath, "utf8"); const data = await fs.readFile(filepath, "utf8");
const buff = Buffer.from(data, "utf8"); const buff = Buffer.from(data, "utf8");
const base64 = buff.toString("base64"); const base64 = buff.toString("base64");
performance.mark("sourceFileReadEnd"); performance.mark("sourceFileReadEnd");
performance.measure( performance.measure(
"Read source file", "Read source file",
"sourceFileReadStart", "sourceFileReadStart",
"sourceFileReadEnd" "sourceFileReadEnd"
); );
performance.mark("lokaliseUploadInitStart"); performance.mark("lokaliseUploadInitStart");
const bgProcess = await lokaliseApi.files().upload(projectId, { const bgProcess = await lokaliseApi.files().upload(projectId, {
data: base64, data: base64,
filename: "en.json", filename: "en.json",
lang_iso: "en", lang_iso: "en",
detect_icu_plurals: true, detect_icu_plurals: true,
format: "json", format: "json",
convert_placeholders: true, convert_placeholders: true,
replace_modified: false, replace_modified: false,
}); });
performance.mark("lokaliseUploadInitEnd"); performance.mark("lokaliseUploadInitEnd");
performance.measure( performance.measure(
"Upload init", "Upload init",
"lokaliseUploadInitStart", "lokaliseUploadInitStart",
"lokaliseUploadInitEnd" "lokaliseUploadInitEnd"
); );
performance.mark("lokaliseUploadStart"); performance.mark("lokaliseUploadStart");
await waitUntilUploadDone(bgProcess.process_id); await waitUntilUploadDone(bgProcess.process_id);
performance.mark("lokaliseUploadEnd"); performance.mark("lokaliseUploadEnd");
performance.measure( performance.measure(
"Upload transfer", "Upload transfer",
"lokaliseUploadStart", "lokaliseUploadStart",
"lokaliseUploadEnd" "lokaliseUploadEnd"
); );
lokaliseLogger.debug("Upload successful"); lokaliseLogger.debug("Upload successful");
} catch (e) { } catch (e) {
lokaliseLogger.error("Upload failed", e); lokaliseLogger.error("Upload failed", e);
} finally { } finally {
performance.mark("uploadEnd"); performance.mark("uploadEnd");
performance.measure("Upload operation", "uploadStart", "uploadEnd"); performance.measure("Upload operation", "uploadStart", "uploadEnd");
} }
performance.measure("done"); performance.measure("done");
await performanceMetrics; await performanceMetrics;
} }
export async function download(extractPath: string, all: boolean = false) { export async function download(extractPath: string, all: boolean = false) {
perf.observe({ type: "measure" }); perf.observe({ type: "measure" });
try { try {
lokaliseLogger.debug( lokaliseLogger.debug(
all all
? "Downloading all translations..." ? "Downloading all translations..."
: "Downloading filtered translations..." : "Downloading filtered translations..."
); );
performance.mark("downloadStart"); performance.mark("downloadStart");
performance.mark("lokaliseDownloadInitStart"); performance.mark("lokaliseDownloadInitStart");
const downloadResponse = await lokaliseApi.files().download(projectId, { const downloadResponse = await lokaliseApi.files().download(projectId, {
format: "json_structured", format: "json_structured",
indentation: "2sp", indentation: "2sp",
placeholder_format: "icu", placeholder_format: "icu",
plural_format: "icu", plural_format: "icu",
icu_numeric: false, icu_numeric: false,
bundle_structure: "%LANG_ISO%.%FORMAT%", bundle_structure: "%LANG_ISO%.%FORMAT%",
directory_prefix: "", directory_prefix: "",
filter_data: all ? [] : ["translated", "nonhidden"], filter_data: all ? [] : ["translated", "nonhidden"],
export_empty_as: "skip", export_empty_as: "skip",
}); });
performance.mark("lokaliseDownloadInitEnd"); performance.mark("lokaliseDownloadInitEnd");
performance.measure( performance.measure(
"Download init", "Download init",
"lokaliseDownloadInitStart", "lokaliseDownloadInitStart",
"lokaliseDownloadInitEnd" "lokaliseDownloadInitEnd"
); );
const { bundle_url } = downloadResponse; const { bundle_url } = downloadResponse;
performance.mark("lokaliseDownloadStart"); performance.mark("lokaliseDownloadStart");
const bundleResponse = await fetch(bundle_url); const bundleResponse = await fetch(bundle_url);
performance.mark("lokaliseDownloadEnd"); performance.mark("lokaliseDownloadEnd");
performance.measure( performance.measure(
"Download transfer", "Download transfer",
"lokaliseDownloadStart", "lokaliseDownloadStart",
"lokaliseDownloadEnd" "lokaliseDownloadEnd"
); );
if (bundleResponse.ok) { if (bundleResponse.ok) {
performance.mark("unpackTranslationsStart"); performance.mark("unpackTranslationsStart");
const arrayBuffer = await bundleResponse.arrayBuffer(); const arrayBuffer = await bundleResponse.arrayBuffer();
const buffer = Buffer.from(new Uint8Array(arrayBuffer)); const buffer = Buffer.from(new Uint8Array(arrayBuffer));
const zip = new AdmZip(buffer); const zip = new AdmZip(buffer);
zip.extractAllTo(extractPath, true); zip.extractAllTo(extractPath, true);
performance.mark("unpackTranslationsEnd"); performance.mark("unpackTranslationsEnd");
performance.measure( performance.measure(
"Unpacking translations", "Unpacking translations",
"unpackTranslationsStart", "unpackTranslationsStart",
"unpackTranslationsEnd" "unpackTranslationsEnd"
); );
lokaliseLogger.debug("Download successful"); lokaliseLogger.debug("Download successful");
} else { } else {
throw bundleResponse; throw bundleResponse;
}
} catch (e) {
lokaliseLogger.error("Download failed", e);
} finally {
performance.mark("downloadEnd");
performance.measure(
"Download operation",
"downloadStart",
"downloadEnd"
);
} }
} catch (e) {
lokaliseLogger.error("Download failed", e);
} finally {
performance.mark("downloadEnd");
performance.measure("done"); performance.measure("Download operation", "downloadStart", "downloadEnd");
}
await performanceMetrics; performance.measure("done");
await performanceMetrics;
} }
export async function deleteBulk(keyNames: string[]) { export async function deleteBulk(keyNames: string[]) {
perf.observe({ type: "measure" }); perf.observe({ type: "measure" });
try { try {
performance.mark("bulkDeleteStart"); performance.mark("bulkDeleteStart");
let keysToDelete: number[] = []; let keysToDelete: number[] = [];
let cursor: string | undefined = undefined; let cursor: string | undefined = undefined;
let hasNext = true; let hasNext = true;
do { do {
const keys = await lokaliseApi.keys().list({ const keys = await lokaliseApi.keys().list({
project_id: projectId, project_id: projectId,
limit: 100, limit: 100,
pagination: "cursor", pagination: "cursor",
cursor, cursor,
}); });
cursor = keys.nextCursor ?? undefined; cursor = keys.nextCursor ?? undefined;
keys.items.forEach((key) => { keys.items.forEach((key) => {
if (key.key_id && key.key_name.web) { if (key.key_id && key.key_name.web) {
if (keyNames.includes(key.key_name.web)) { if (keyNames.includes(key.key_name.web)) {
keysToDelete.push(key.key_id); keysToDelete.push(key.key_id);
} }
} }
}); });
if (!keys.hasNextCursor()) { if (!keys.hasNextCursor()) {
hasNext = false; hasNext = false;
} }
} while (hasNext); } while (hasNext);
const response = await lokaliseApi const response = await lokaliseApi
.keys() .keys()
.bulk_delete(keysToDelete, { project_id: projectId }); .bulk_delete(keysToDelete, { project_id: projectId });
lokaliseLogger.debug( lokaliseLogger.debug(
`Bulk delete successful, removed ${keysToDelete.length} keys` `Bulk delete successful, removed ${keysToDelete.length} keys`
); );
return response; return response;
} catch (e) { } catch (e) {
lokaliseLogger.error("Bulk delete failed", e); lokaliseLogger.error("Bulk delete failed", e);
} finally { } finally {
performance.mark("bulkDeleteEnd"); performance.mark("bulkDeleteEnd");
performance.measure( performance.measure(
"Bulk delete operation", "Bulk delete operation",
"bulkDeleteStart", "bulkDeleteStart",
"bulkDeleteEnd" "bulkDeleteEnd"
); );
} }
} }