Support ICU in TS components
This commit is contained in:
parent
e435cf44c0
commit
eaa529528c
|
@ -100,6 +100,7 @@
|
|||
"html-loader": "^3.0.1",
|
||||
"html-webpack-plugin": "^5.3.1",
|
||||
"https-browserify": "^1.0.0",
|
||||
"intl-messageformat": "^10.0.1",
|
||||
"jschannel": "^1.0.2",
|
||||
"linkify-html": "^3.0.2",
|
||||
"linkify-plugin-mention": "^3.0.2",
|
||||
|
|
|
@ -23,10 +23,10 @@
|
|||
</h2>
|
||||
|
||||
<div class="actor-counters">
|
||||
<div class="followers" i18n>{videoChannel.followersCount, plural, =1 {1 subscriber} other {{{ videoChannel.followersCount }} subscribers}}</div>
|
||||
<div class="followers" i18n>{videoChannel.followersCount, plural, =0 {No subscribers} =1 {1 subscriber} other {{{ videoChannel.followersCount }} subscribers}}</div>
|
||||
|
||||
<span class="videos-count" *ngIf="getTotalVideosOf(videoChannel) !== undefined" i18n>
|
||||
{getTotalVideosOf(videoChannel), plural, =1 {1 videos} other {{{ getTotalVideosOf(videoChannel) }} videos}}
|
||||
{getTotalVideosOf(videoChannel), plural, =0 {No videos} =1 {1 video} other {{{ getTotalVideosOf(videoChannel) }} videos}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -33,10 +33,10 @@
|
|||
</div>
|
||||
|
||||
<div class="actor-counters">
|
||||
<span i18n>{naiveAggregatedSubscribers(), plural, =1 {1 subscriber} other {{{ naiveAggregatedSubscribers() }} subscribers}}</span>
|
||||
<span i18n>{naiveAggregatedSubscribers(), plural, =0 {No subscribers} =1 {1 subscriber} other {{{ naiveAggregatedSubscribers() }} subscribers}}</span>
|
||||
|
||||
<span class="videos-count" *ngIf="accountVideosCount !== undefined" i18n>
|
||||
{accountVideosCount, plural, =1 {1 videos} other {{{ accountVideosCount }} videos}}
|
||||
{accountVideosCount, plural, =0 {No videos} =1 {1 video} other {{{ accountVideosCount }} videos}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -30,8 +30,6 @@ export class AccountsComponent implements OnInit, OnDestroy {
|
|||
links: ListOverflowItem[] = []
|
||||
hideMenu = false
|
||||
|
||||
accountFollowerTitle = ''
|
||||
|
||||
accountVideosCount: number
|
||||
accountDescriptionHTML = ''
|
||||
accountDescriptionExpanded = false
|
||||
|
@ -121,12 +119,6 @@ export class AccountsComponent implements OnInit, OnDestroy {
|
|||
this.notifier.success($localize`Username copied`)
|
||||
}
|
||||
|
||||
subscribersDisplayFor (count: number) {
|
||||
if (count === 1) return $localize`1 subscriber`
|
||||
|
||||
return $localize`${count} subscribers`
|
||||
}
|
||||
|
||||
searchChanged (search: string) {
|
||||
const queryParams = { search }
|
||||
|
||||
|
@ -150,8 +142,6 @@ export class AccountsComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
private async onAccount (account: Account) {
|
||||
this.accountFollowerTitle = $localize`${account.followersCount} direct account followers`
|
||||
|
||||
this.accountDescriptionHTML = await this.markdown.textMarkdownToHTML(account.description)
|
||||
|
||||
// After the markdown renderer to avoid layout changes
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Injectable } from '@angular/core'
|
||||
import { FormGroup } from '@angular/forms'
|
||||
import { prepareIcu } from '@app/helpers'
|
||||
|
||||
export type ResolutionOption = {
|
||||
id: string
|
||||
|
@ -86,9 +87,10 @@ export class EditConfigurationService {
|
|||
return {
|
||||
value,
|
||||
atMost: noneOnAuto, // auto switches everything to a least estimation since ffmpeg will take as many threads as possible
|
||||
unit: value > 1
|
||||
? $localize`threads`
|
||||
: $localize`thread`
|
||||
unit: prepareIcu($localize`{value, plural, =1 {thread} other {threads}}`)(
|
||||
{ value },
|
||||
$localize`threads`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'
|
||||
import { Notifier } from '@app/core'
|
||||
import { prepareIcu } from '@app/helpers'
|
||||
import { splitAndGetNotEmpty, UNIQUE_HOSTS_OR_HANDLE_VALIDATOR } from '@app/shared/form-validators/host-validators'
|
||||
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
||||
import { InstanceFollowService } from '@app/shared/shared-instance'
|
||||
|
@ -60,7 +61,13 @@ export class FollowModalComponent extends FormReactive implements OnInit {
|
|||
this.followService.follow(hostsOrHandles)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.notifier.success($localize`Follow request(s) sent!`)
|
||||
this.notifier.success(
|
||||
prepareIcu($localize`{count, plural, =1 {Follow request} other {Follow requests}} sent!`)(
|
||||
{ count: hostsOrHandles.length },
|
||||
$localize`Follow request(s) sent!`
|
||||
)
|
||||
)
|
||||
|
||||
this.newFollow.emit()
|
||||
},
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import { DropdownAction } from '@app/shared/shared-main'
|
|||
import { BulkService } from '@app/shared/shared-moderation'
|
||||
import { VideoCommentAdmin, VideoCommentService } from '@app/shared/shared-video-comment'
|
||||
import { FeedFormat, UserRight } from '@shared/models'
|
||||
import { prepareIcu } from '@app/helpers'
|
||||
|
||||
@Component({
|
||||
selector: 'my-video-comment-list',
|
||||
|
@ -145,7 +146,13 @@ export class VideoCommentListComponent extends RestTable implements OnInit {
|
|||
this.videoCommentService.deleteVideoComments(commentArgs)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.notifier.success($localize`${commentArgs.length} comments deleted.`)
|
||||
this.notifier.success(
|
||||
prepareIcu($localize`{count, plural, =1 {1 comment} other {{count} comments}} deleted.`)(
|
||||
{ count: commentArgs.length },
|
||||
$localize`${commentArgs.length} comment(s) deleted.`
|
||||
)
|
||||
)
|
||||
|
||||
this.reloadData()
|
||||
},
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import { SortMeta } from 'primeng/api'
|
|||
import { Component, OnInit, ViewChild } from '@angular/core'
|
||||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import { AuthService, ConfirmService, LocalStorageService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
|
||||
import { getAPIHost } from '@app/helpers'
|
||||
import { prepareIcu, getAPIHost } from '@app/helpers'
|
||||
import { AdvancedInputFilter } from '@app/shared/shared-forms'
|
||||
import { Actor, DropdownAction } from '@app/shared/shared-main'
|
||||
import { AccountMutedStatus, BlocklistService, UserBanModalComponent, UserModerationDisplayType } from '@app/shared/shared-moderation'
|
||||
|
@ -209,13 +209,25 @@ export class UserListComponent extends RestTable implements OnInit {
|
|||
}
|
||||
|
||||
async unbanUsers (users: User[]) {
|
||||
const res = await this.confirmService.confirm($localize`Do you really want to unban ${users.length} users?`, $localize`Unban`)
|
||||
const res = await this.confirmService.confirm(
|
||||
prepareIcu($localize`Do you really want to unban {count, plural, =1 {1 user} other {{count} users}}?`)(
|
||||
{ count: users.length },
|
||||
$localize`Do you really want to unban ${users.length} users?`
|
||||
),
|
||||
$localize`Unban`
|
||||
)
|
||||
|
||||
if (res === false) return
|
||||
|
||||
this.userAdminService.unbanUsers(users)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.notifier.success($localize`${users.length} users unbanned.`)
|
||||
this.notifier.success(
|
||||
prepareIcu($localize`{count, plural, =1 {1 user} other {{count} users}} unbanned.`)(
|
||||
{ count: users.length },
|
||||
$localize`${users.length} users unbanned.`
|
||||
)
|
||||
)
|
||||
this.reloadData()
|
||||
},
|
||||
|
||||
|
@ -224,21 +236,28 @@ export class UserListComponent extends RestTable implements OnInit {
|
|||
}
|
||||
|
||||
async removeUsers (users: User[]) {
|
||||
for (const user of users) {
|
||||
if (user.username === 'root') {
|
||||
this.notifier.error($localize`You cannot delete root.`)
|
||||
return
|
||||
}
|
||||
if (users.some(u => u.username === 'root')) {
|
||||
this.notifier.error($localize`You cannot delete root.`)
|
||||
return
|
||||
}
|
||||
|
||||
const message = $localize`If you remove these users, you will not be able to create others with the same username!`
|
||||
const message = $localize`<p>You can't create users or channels with a username that already used by a deleted user/channel.</p>` +
|
||||
$localize`It means the following usernames will be permanently deleted and cannot be recovered:` +
|
||||
'<ul>' + users.map(u => '<li>' + u.username + '</li>').join('') + '</ul>'
|
||||
|
||||
const res = await this.confirmService.confirm(message, $localize`Delete`)
|
||||
if (res === false) return
|
||||
|
||||
this.userAdminService.removeUser(users)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.notifier.success($localize`${users.length} users deleted.`)
|
||||
this.notifier.success(
|
||||
prepareIcu($localize`{count, plural, =1 {1 user} other {{count} users}} deleted.`)(
|
||||
{ count: users.length },
|
||||
$localize`${users.length} users deleted.`
|
||||
)
|
||||
)
|
||||
|
||||
this.reloadData()
|
||||
},
|
||||
|
||||
|
@ -250,7 +269,13 @@ export class UserListComponent extends RestTable implements OnInit {
|
|||
this.userAdminService.updateUsers(users, { emailVerified: true })
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.notifier.success($localize`${users.length} users email set as verified.`)
|
||||
this.notifier.success(
|
||||
prepareIcu($localize`{count, plural, =1 {1 user} other {{count} users}} email set as verified.`)(
|
||||
{ count: users.length },
|
||||
$localize`${users.length} users email set as verified.`
|
||||
)
|
||||
)
|
||||
|
||||
this.reloadData()
|
||||
},
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import { finalize } from 'rxjs/operators'
|
|||
import { Component, OnInit, ViewChild } from '@angular/core'
|
||||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import { AuthService, ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
|
||||
import { prepareIcu } from '@app/helpers'
|
||||
import { AdvancedInputFilter } from '@app/shared/shared-forms'
|
||||
import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
|
||||
import { VideoBlockComponent, VideoBlockService } from '@app/shared/shared-moderation'
|
||||
|
@ -196,14 +197,24 @@ export class VideoListComponent extends RestTable implements OnInit {
|
|||
}
|
||||
|
||||
private async removeVideos (videos: Video[]) {
|
||||
const message = $localize`Are you sure you want to delete these ${videos.length} videos?`
|
||||
const message = prepareIcu($localize`Are you sure you want to delete {count, plural, =1 {this video} other {these {count} videos}}?`)(
|
||||
{ count: videos.length },
|
||||
$localize`Are you sure you want to delete these ${videos.length} videos?`
|
||||
)
|
||||
|
||||
const res = await this.confirmService.confirm(message, $localize`Delete`)
|
||||
if (res === false) return
|
||||
|
||||
this.videoService.removeVideo(videos.map(v => v.id))
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.notifier.success($localize`Deleted ${videos.length} videos.`)
|
||||
this.notifier.success(
|
||||
prepareIcu($localize`Deleted {count, plural, =1 {1 video} other {{count} videos}}.`)(
|
||||
{ count: videos.length },
|
||||
$localize`Deleted ${videos.length} videos.`
|
||||
)
|
||||
)
|
||||
|
||||
this.reloadData()
|
||||
},
|
||||
|
||||
|
@ -215,7 +226,13 @@ export class VideoListComponent extends RestTable implements OnInit {
|
|||
this.videoBlockService.unblockVideo(videos.map(v => v.id))
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.notifier.success($localize`Unblocked ${videos.length} videos.`)
|
||||
this.notifier.success(
|
||||
prepareIcu($localize`Unblocked {count, plural, =1 {1 video} other {{count} videos}}.`)(
|
||||
{ count: videos.length },
|
||||
$localize`Unblocked ${videos.length} videos.`
|
||||
)
|
||||
)
|
||||
|
||||
this.reloadData()
|
||||
},
|
||||
|
||||
|
@ -224,9 +241,21 @@ export class VideoListComponent extends RestTable implements OnInit {
|
|||
}
|
||||
|
||||
private async removeVideoFiles (videos: Video[], type: 'hls' | 'webtorrent') {
|
||||
const message = type === 'hls'
|
||||
? $localize`Are you sure you want to delete ${videos.length} HLS streaming playlists?`
|
||||
: $localize`Are you sure you want to delete WebTorrent files of ${videos.length} videos?`
|
||||
let message: string
|
||||
|
||||
if (type === 'hls') {
|
||||
// eslint-disable-next-line max-len
|
||||
message = prepareIcu($localize`Are you sure you want to delete {count, plural, =1 {1 HLS streaming playlist} other {{count} HLS streaming playlists}}?`)(
|
||||
{ count: videos.length },
|
||||
$localize`Are you sure you want to delete ${videos.length} HLS streaming playlists?`
|
||||
)
|
||||
} else {
|
||||
// eslint-disable-next-line max-len
|
||||
message = prepareIcu($localize`Are you sure you want to delete WebTorrent files of {count, plural, =1 {1 video} other {{count} videos}}?`)(
|
||||
{ count: videos.length },
|
||||
$localize`Are you sure you want to delete WebTorrent files of ${videos.length} videos?`
|
||||
)
|
||||
}
|
||||
|
||||
const res = await this.confirmService.confirm(message, $localize`Delete`)
|
||||
if (res === false) return
|
||||
|
|
|
@ -37,7 +37,7 @@ export class MyAccountNotificationPreferencesComponent implements OnInit {
|
|||
myVideoPublished: $localize`Video published (after transcoding/scheduled update)`,
|
||||
myVideoImportFinished: $localize`Video import finished`,
|
||||
newUserRegistration: $localize`A new user registered on your instance`,
|
||||
newFollow: $localize`You or your channel(s) has a new follower`,
|
||||
newFollow: $localize`You or one of your channels has a new follower`,
|
||||
commentMention: $localize`Someone mentioned you in video comments`,
|
||||
newInstanceFollower: $localize`Your instance has a new follower`,
|
||||
autoInstanceFollowing: $localize`Your instance automatically followed another instance`,
|
||||
|
|
|
@ -93,8 +93,8 @@ export class MyHistoryComponent implements OnInit, DisableForReuseHook {
|
|||
.subscribe({
|
||||
next: () => {
|
||||
const message = this.videosHistoryEnabled === true
|
||||
? $localize`Videos history is enabled`
|
||||
: $localize`Videos history is disabled`
|
||||
? $localize`Video history is enabled`
|
||||
: $localize`Video history is disabled`
|
||||
|
||||
this.notifier.success(message)
|
||||
|
||||
|
@ -117,8 +117,8 @@ export class MyHistoryComponent implements OnInit, DisableForReuseHook {
|
|||
}
|
||||
|
||||
async clearAllHistory () {
|
||||
const title = $localize`Delete videos history`
|
||||
const message = $localize`Are you sure you want to delete all your videos history?`
|
||||
const title = $localize`Delete video history`
|
||||
const message = $localize`Are you sure you want to delete all your video history?`
|
||||
|
||||
const res = await this.confirmService.confirm(message, title)
|
||||
if (res !== true) return
|
||||
|
@ -126,7 +126,7 @@ export class MyHistoryComponent implements OnInit, DisableForReuseHook {
|
|||
this.userHistoryService.clearAll()
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.notifier.success($localize`Videos history deleted`)
|
||||
this.notifier.success($localize`Video history deleted`)
|
||||
|
||||
this.reloadData()
|
||||
},
|
||||
|
|
|
@ -4,7 +4,7 @@ import { Component, OnInit, ViewChild } from '@angular/core'
|
|||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import { AuthService, ComponentPagination, ConfirmService, Notifier, ScreenService, ServerService, User } from '@app/core'
|
||||
import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook'
|
||||
import { immutableAssign } from '@app/helpers'
|
||||
import { prepareIcu, immutableAssign } from '@app/helpers'
|
||||
import { AdvancedInputFilter } from '@app/shared/shared-forms'
|
||||
import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
|
||||
import { LiveStreamInformationComponent } from '@app/shared/shared-video-live'
|
||||
|
@ -167,7 +167,10 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
|
|||
.map(k => parseInt(k, 10))
|
||||
|
||||
const res = await this.confirmService.confirm(
|
||||
$localize`Do you really want to delete ${toDeleteVideosIds.length} videos?`,
|
||||
prepareIcu($localize`Do you really want to delete {length, plural, =1 {this video} other {{length} videos}}?`)(
|
||||
{ length: toDeleteVideosIds.length },
|
||||
$localize`Do you really want to delete ${toDeleteVideosIds.length} videos?`
|
||||
),
|
||||
$localize`Delete`
|
||||
)
|
||||
if (res === false) return
|
||||
|
@ -184,7 +187,13 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
|
|||
.pipe(toArray())
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.notifier.success($localize`${toDeleteVideosIds.length} videos deleted.`)
|
||||
this.notifier.success(
|
||||
prepareIcu($localize`{length, plural, =1 {Video has been deleted} other {{length} videos have been deleted}}`)(
|
||||
{ length: toDeleteVideosIds.length },
|
||||
$localize`${toDeleteVideosIds.length} have been deleted.`
|
||||
)
|
||||
)
|
||||
|
||||
this.selection = {}
|
||||
},
|
||||
|
||||
|
|
|
@ -248,11 +248,11 @@ export class SearchComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
private updateTitle () {
|
||||
const suffix = this.currentSearch
|
||||
? ' ' + this.currentSearch
|
||||
: ''
|
||||
const title = this.currentSearch
|
||||
? $localize`Search ${this.currentSearch}`
|
||||
: $localize`Search`
|
||||
|
||||
this.metaService.setTitle($localize`Search` + suffix)
|
||||
this.metaService.setTitle(title)
|
||||
}
|
||||
|
||||
private updateUrlFromAdvancedSearch () {
|
||||
|
|
|
@ -72,10 +72,10 @@
|
|||
</div>
|
||||
|
||||
<div class="actor-counters">
|
||||
<span i18n>{videoChannel.followersCount, plural, =1 {1 subscriber} other {{{ videoChannel.followersCount }} subscribers}}</span>
|
||||
<span i18n>{videoChannel.followersCount, plural, =0 {No subscribers} =1 {1 subscriber} other {{{ videoChannel.followersCount }} subscribers}}</span>
|
||||
|
||||
<span class="videos-count" *ngIf="channelVideosCount !== undefined" i18n>
|
||||
{channelVideosCount, plural, =1 {1 videos} other {{{ channelVideosCount }} videos}}
|
||||
{channelVideosCount, plural, =0 {No videos} =1 {1 video} other {{{ channelVideosCount }} videos}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -204,13 +204,28 @@ export class VideosListCommonPageComponent implements OnInit, OnDestroy, Disable
|
|||
if ([ 'hot', 'trending', 'likes', 'views' ].includes(sanitizedSort)) {
|
||||
this.title = $localize`Trending`
|
||||
|
||||
if (sanitizedSort === 'hot') this.titleTooltip = $localize`Videos with the most interactions for recent videos`
|
||||
if (sanitizedSort === 'likes') this.titleTooltip = $localize`Videos that have the most likes`
|
||||
if (sanitizedSort === 'views') this.titleTooltip = undefined
|
||||
if (sanitizedSort === 'hot') {
|
||||
this.titleTooltip = $localize`Videos with the most interactions for recent videos`
|
||||
return
|
||||
}
|
||||
|
||||
if (sanitizedSort === 'likes') {
|
||||
this.titleTooltip = $localize`Videos that have the most likes`
|
||||
return
|
||||
}
|
||||
|
||||
if (sanitizedSort === 'views') {
|
||||
this.titleTooltip = undefined
|
||||
return
|
||||
}
|
||||
|
||||
if (sanitizedSort === 'trending') {
|
||||
if (this.trendingDays === 1) this.titleTooltip = $localize`Videos with the most views during the last 24 hours`
|
||||
else this.titleTooltip = $localize`Videos with the most views during the last ${this.trendingDays} days`
|
||||
if (this.trendingDays === 1) {
|
||||
this.titleTooltip = $localize`Videos with the most views during the last 24 hours`
|
||||
return
|
||||
}
|
||||
|
||||
this.titleTooltip = $localize`Videos with the most views during the last ${this.trendingDays} days`
|
||||
}
|
||||
|
||||
return
|
||||
|
|
|
@ -34,50 +34,18 @@ export class RestExtractor {
|
|||
return target
|
||||
}
|
||||
|
||||
handleError (err: any) {
|
||||
let errorMessage
|
||||
|
||||
if (err.error instanceof Error) {
|
||||
// A client-side or network error occurred. Handle it accordingly.
|
||||
errorMessage = err.error.detail || err.error.title
|
||||
console.error('An error occurred:', errorMessage)
|
||||
} else if (typeof err.error === 'string') {
|
||||
errorMessage = err.error
|
||||
} else if (err.status !== undefined) {
|
||||
// A server-side error occurred.
|
||||
if (err.error?.errors) {
|
||||
const errors = err.error.errors
|
||||
const errorsArray: string[] = []
|
||||
|
||||
Object.keys(errors).forEach(key => {
|
||||
errorsArray.push(errors[key].msg)
|
||||
})
|
||||
|
||||
errorMessage = errorsArray.join('. ')
|
||||
} else if (err.error?.error) {
|
||||
errorMessage = err.error.error
|
||||
} else if (err.status === HttpStatusCode.PAYLOAD_TOO_LARGE_413) {
|
||||
// eslint-disable-next-line max-len
|
||||
errorMessage = $localize`Media is too large for the server. Please contact you administrator if you want to increase the limit size.`
|
||||
} else if (err.status === HttpStatusCode.TOO_MANY_REQUESTS_429) {
|
||||
const secondsLeft = err.headers.get('retry-after')
|
||||
if (secondsLeft) {
|
||||
const minutesLeft = Math.floor(parseInt(secondsLeft, 10) / 60)
|
||||
errorMessage = $localize`Too many attempts, please try again after ${minutesLeft} minutes.`
|
||||
} else {
|
||||
errorMessage = $localize`Too many attempts, please try again later.`
|
||||
}
|
||||
} else if (err.status === HttpStatusCode.INTERNAL_SERVER_ERROR_500) {
|
||||
errorMessage = $localize`Server error. Please retry later.`
|
||||
}
|
||||
|
||||
errorMessage = errorMessage || 'Unknown error.'
|
||||
console.error(`Backend returned code ${err.status}, errorMessage is: ${errorMessage}`)
|
||||
} else {
|
||||
console.error(err)
|
||||
errorMessage = err
|
||||
redirectTo404IfNotFound (obj: { status: number }, type: 'video' | 'other', status = [ HttpStatusCode.NOT_FOUND_404 ]) {
|
||||
if (obj?.status && status.includes(obj.status)) {
|
||||
// Do not use redirectService to avoid circular dependencies
|
||||
this.router.navigate([ '/404' ], { state: { type, obj }, skipLocationChange: true })
|
||||
}
|
||||
|
||||
return observableThrowError(() => obj)
|
||||
}
|
||||
|
||||
handleError (err: any) {
|
||||
const errorMessage = this.buildErrorMessage(err)
|
||||
|
||||
const errorObj: { message: string, status: string, body: string } = {
|
||||
message: errorMessage,
|
||||
status: undefined,
|
||||
|
@ -92,12 +60,63 @@ export class RestExtractor {
|
|||
return observableThrowError(() => errorObj)
|
||||
}
|
||||
|
||||
redirectTo404IfNotFound (obj: { status: number }, type: 'video' | 'other', status = [ HttpStatusCode.NOT_FOUND_404 ]) {
|
||||
if (obj?.status && status.includes(obj.status)) {
|
||||
// Do not use redirectService to avoid circular dependencies
|
||||
this.router.navigate([ '/404' ], { state: { type, obj }, skipLocationChange: true })
|
||||
private buildErrorMessage (err: any) {
|
||||
if (err.error instanceof Error) {
|
||||
// A client-side or network error occurred. Handle it accordingly.
|
||||
const errorMessage = err.error.detail || err.error.title
|
||||
console.error('An error occurred:', errorMessage)
|
||||
|
||||
return errorMessage
|
||||
}
|
||||
|
||||
return observableThrowError(() => obj)
|
||||
if (typeof err.error === 'string') {
|
||||
return err.error
|
||||
}
|
||||
|
||||
if (err.status !== undefined) {
|
||||
const errorMessage = this.buildServerErrorMessage(err)
|
||||
console.error(`Backend returned code ${err.status}, errorMessage is: ${errorMessage}`)
|
||||
|
||||
return errorMessage
|
||||
}
|
||||
|
||||
console.error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
private buildServerErrorMessage (err: any) {
|
||||
// A server-side error occurred.
|
||||
if (err.error?.errors) {
|
||||
const errors = err.error.errors
|
||||
|
||||
return Object.keys(errors)
|
||||
.map(key => errors[key].msg)
|
||||
.join('. ')
|
||||
}
|
||||
|
||||
if (err.error?.error) {
|
||||
return err.error.error
|
||||
}
|
||||
|
||||
if (err.status === HttpStatusCode.PAYLOAD_TOO_LARGE_413) {
|
||||
return $localize`Media is too large for the server. Please contact you administrator if you want to increase the limit size.`
|
||||
}
|
||||
|
||||
if (err.status === HttpStatusCode.TOO_MANY_REQUESTS_429) {
|
||||
const secondsLeft = err.headers.get('retry-after')
|
||||
|
||||
if (secondsLeft) {
|
||||
const minutesLeft = Math.floor(parseInt(secondsLeft, 10) / 60)
|
||||
return $localize`Too many attempts, please try again after ${minutesLeft} minutes.`
|
||||
}
|
||||
|
||||
return $localize`Too many attempts, please try again later.`
|
||||
}
|
||||
|
||||
if (err.status === HttpStatusCode.INTERNAL_SERVER_ERROR_500) {
|
||||
return $localize`Server error. Please retry later.`
|
||||
}
|
||||
|
||||
return $localize`Unknown server error`
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { environment } from '../../environments/environment'
|
||||
import IntlMessageFormat from 'intl-messageformat'
|
||||
|
||||
function isOnDevLocale () {
|
||||
return environment.production === false && window.location.search === '?lang=fr'
|
||||
|
@ -8,7 +9,31 @@ function getDevLocale () {
|
|||
return 'fr-FR'
|
||||
}
|
||||
|
||||
function prepareIcu (icu: string) {
|
||||
let alreadyWarned = false
|
||||
|
||||
try {
|
||||
const msg = new IntlMessageFormat(icu, $localize.locale)
|
||||
|
||||
return (context: { [id: string]: number | string }, fallback: string) => {
|
||||
try {
|
||||
return msg.format(context) as string
|
||||
} catch (err) {
|
||||
if (!alreadyWarned) console.warn('Cannot format ICU %s.', icu, err)
|
||||
|
||||
alreadyWarned = true
|
||||
return fallback
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('Cannot build intl message %s.', icu, err)
|
||||
|
||||
return (_context: unknown, fallback: string) => fallback
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
getDevLocale,
|
||||
prepareIcu,
|
||||
isOnDevLocale
|
||||
}
|
||||
|
|
|
@ -2,36 +2,43 @@ import { HttpErrorResponse } from '@angular/common/http'
|
|||
import { Notifier } from '@app/core'
|
||||
import { HttpStatusCode } from '@shared/models'
|
||||
|
||||
function genericUploadErrorHandler (parameters: {
|
||||
function genericUploadErrorHandler (options: {
|
||||
err: Pick<HttpErrorResponse, 'message' | 'status' | 'headers'>
|
||||
name: string
|
||||
notifier: Notifier
|
||||
sticky?: boolean
|
||||
}) {
|
||||
const { err, name, notifier, sticky } = { sticky: false, ...parameters }
|
||||
const title = $localize`The upload failed`
|
||||
let message = err.message
|
||||
|
||||
if (err instanceof ErrorEvent) { // network error
|
||||
message = $localize`The connection was interrupted`
|
||||
notifier.error(message, title, null, sticky)
|
||||
} else if (err.status === HttpStatusCode.INTERNAL_SERVER_ERROR_500) {
|
||||
message = $localize`The server encountered an error`
|
||||
notifier.error(message, title, null, sticky)
|
||||
} else if (err.status === HttpStatusCode.REQUEST_TIMEOUT_408) {
|
||||
message = $localize`Your ${name} file couldn't be transferred before the set timeout (usually 10min)`
|
||||
notifier.error(message, title, null, sticky)
|
||||
} else if (err.status === HttpStatusCode.PAYLOAD_TOO_LARGE_413) {
|
||||
const maxFileSize = err.headers?.get('X-File-Maximum-Size') || '8G'
|
||||
message = $localize`Your ${name} file was too large (max. size: ${maxFileSize})`
|
||||
notifier.error(message, title, null, sticky)
|
||||
} else {
|
||||
notifier.error(err.message, title)
|
||||
}
|
||||
const { err, name, notifier, sticky = false } = options
|
||||
const title = $localize`Upload failed`
|
||||
const message = buildMessage(name, err)
|
||||
|
||||
notifier.error(message, title, null, sticky)
|
||||
return message
|
||||
}
|
||||
|
||||
export {
|
||||
genericUploadErrorHandler
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function buildMessage (name: string, err: Pick<HttpErrorResponse, 'message' | 'status' | 'headers'>) {
|
||||
if (err instanceof ErrorEvent) { // network error
|
||||
return $localize`The connection was interrupted`
|
||||
}
|
||||
|
||||
if (err.status === HttpStatusCode.INTERNAL_SERVER_ERROR_500) {
|
||||
return $localize`The server encountered an error`
|
||||
}
|
||||
|
||||
if (err.status === HttpStatusCode.REQUEST_TIMEOUT_408) {
|
||||
return $localize`Your ${name} file couldn't be transferred before the server proxy timeout`
|
||||
}
|
||||
|
||||
if (err.status === HttpStatusCode.PAYLOAD_TOO_LARGE_413) {
|
||||
const maxFileSize = err.headers?.get('X-File-Maximum-Size') || '8G'
|
||||
return $localize`Your ${name} file was too large (max. size: ${maxFileSize})`
|
||||
}
|
||||
|
||||
return err.message
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Component, forwardRef, Input } from '@angular/core'
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
|
||||
import { Notifier } from '@app/core'
|
||||
import { prepareIcu } from '@app/helpers'
|
||||
import { SelectOptionsItem } from '../../../../types/select-options-item.model'
|
||||
import { ItemSelectCheckboxValue } from './select-checkbox.component'
|
||||
|
||||
|
@ -78,7 +79,12 @@ export class SelectCheckboxAllComponent implements ControlValueAccessor {
|
|||
if (!outputItems) return true
|
||||
|
||||
if (outputItems.length >= this.maxItems) {
|
||||
this.notifier.error($localize`You can't select more than ${this.maxItems} items`)
|
||||
this.notifier.error(
|
||||
prepareIcu($localize`You can't select more than {maxItems, plural, =1 {1 item} other {{maxItems} items}}`)(
|
||||
{ maxItems: this.maxItems },
|
||||
$localize`You can't select more than ${this.maxItems} items`
|
||||
)
|
||||
)
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { ServerService } from '@app/core'
|
||||
import { prepareIcu } from '@app/helpers'
|
||||
import { ServerConfig } from '@shared/models'
|
||||
import { PeertubeModalService } from '../shared-main/peertube-modal/peertube-modal.service'
|
||||
|
||||
|
@ -65,15 +66,20 @@ export class InstanceFeaturesTableComponent implements OnInit {
|
|||
|
||||
private getApproximateTime (seconds: number) {
|
||||
const hours = Math.floor(seconds / 3600)
|
||||
let pluralSuffix = ''
|
||||
if (hours > 1) pluralSuffix = 's'
|
||||
if (hours > 0) return `~ ${hours} hour${pluralSuffix}`
|
||||
|
||||
if (hours !== 0) {
|
||||
return prepareIcu($localize`~ {hours, plural, =1 {1 hour} other {{hours} hours}}`)(
|
||||
{ hours },
|
||||
$localize`~ ${hours} hours`
|
||||
)
|
||||
}
|
||||
|
||||
const minutes = Math.floor(seconds % 3600 / 60)
|
||||
|
||||
if (minutes === 1) return $localize`~ 1 minute`
|
||||
|
||||
return $localize`~ ${minutes} minutes`
|
||||
return prepareIcu($localize`~ {minutes, plural, =1 {1 minute} other {{minutes} minutes}}`)(
|
||||
{ minutes },
|
||||
$localize`~ ${minutes} minutes`
|
||||
)
|
||||
}
|
||||
|
||||
private buildQuotaHelpIndication () {
|
||||
|
|
|
@ -1,37 +1,51 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { prepareIcu } from '@app/helpers'
|
||||
|
||||
// Thanks: https://stackoverflow.com/questions/3177836/how-to-format-time-since-xxx-e-g-4-minutes-ago-similar-to-stack-exchange-site
|
||||
@Pipe({ name: 'myFromNow' })
|
||||
export class FromNowPipe implements PipeTransform {
|
||||
private yearICU = prepareIcu($localize`{interval, plural, =1 {1 year ago} other {{interval} years ago}}`)
|
||||
private monthICU = prepareIcu($localize`{interval, plural, =1 {1 month ago} other {{interval} months ago}}`)
|
||||
private weekICU = prepareIcu($localize`{interval, plural, =1 {1 week ago} other {{interval} weeks ago}}`)
|
||||
private dayICU = prepareIcu($localize`{interval, plural, =1 {1 day ago} other {{interval} days ago}}`)
|
||||
private hourICU = prepareIcu($localize`{interval, plural, =1 {1 hour ago} other {{interval} hours ago}}`)
|
||||
|
||||
transform (arg: number | Date | string) {
|
||||
const argDate = new Date(arg)
|
||||
const seconds = Math.floor((Date.now() - argDate.getTime()) / 1000)
|
||||
|
||||
let interval = Math.floor(seconds / 31536000)
|
||||
if (interval > 1) return $localize`${interval} years ago`
|
||||
if (interval === 1) return $localize`1 year ago`
|
||||
if (interval >= 1) {
|
||||
return this.yearICU({ interval }, $localize`${interval} year(s) ago`)
|
||||
}
|
||||
|
||||
interval = Math.floor(seconds / 2419200)
|
||||
// 12 months = 360 days, but a year ~ 365 days
|
||||
// Display "1 year ago" rather than "12 months ago"
|
||||
if (interval >= 12) return $localize`1 year ago`
|
||||
if (interval > 1) return $localize`${interval} months ago`
|
||||
if (interval === 1) return $localize`1 month ago`
|
||||
|
||||
if (interval >= 1) {
|
||||
return this.monthICU({ interval }, $localize`${interval} month(s) ago`)
|
||||
}
|
||||
|
||||
interval = Math.floor(seconds / 604800)
|
||||
// 4 weeks ~ 28 days, but our month is 30 days
|
||||
// Display "1 month ago" rather than "4 weeks ago"
|
||||
if (interval >= 4) return $localize`1 month ago`
|
||||
if (interval > 1) return $localize`${interval} weeks ago`
|
||||
if (interval === 1) return $localize`1 week ago`
|
||||
|
||||
if (interval >= 1) {
|
||||
return this.weekICU({ interval }, $localize`${interval} week(s) ago`)
|
||||
}
|
||||
|
||||
interval = Math.floor(seconds / 86400)
|
||||
if (interval > 1) return $localize`${interval} days ago`
|
||||
if (interval === 1) return $localize`1 day ago`
|
||||
if (interval >= 1) {
|
||||
return this.dayICU({ interval }, $localize`${interval} day(s) ago`)
|
||||
}
|
||||
|
||||
interval = Math.floor(seconds / 3600)
|
||||
if (interval > 1) return $localize`${interval} hours ago`
|
||||
if (interval === 1) return $localize`1 hour ago`
|
||||
if (interval >= 1) {
|
||||
return this.hourICU({ interval }, $localize`${interval} hour(s) ago`)
|
||||
}
|
||||
|
||||
interval = Math.floor(seconds / 60)
|
||||
if (interval >= 1) return $localize`${interval} min ago`
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { AuthUser } from '@app/core'
|
||||
import { User } from '@app/core/users/user.model'
|
||||
import { durationToString, getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers'
|
||||
import { durationToString, prepareIcu, getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers'
|
||||
import { Actor } from '@app/shared/shared-main/account/actor.model'
|
||||
import { buildVideoWatchPath } from '@shared/core-utils'
|
||||
import { peertubeTranslate } from '@shared/core-utils/i18n'
|
||||
|
@ -19,6 +19,9 @@ import {
|
|||
} from '@shared/models'
|
||||
|
||||
export class Video implements VideoServerModel {
|
||||
private static readonly viewsICU = prepareIcu($localize`{views, plural, =0 {No view} =1 {1 view} other {{views} views}}`)
|
||||
private static readonly viewersICU = prepareIcu($localize`{viewers, plural, =0 {No viewers} =1 {1 viewer} other {{viewers} viewers}}`)
|
||||
|
||||
byVideoChannel: string
|
||||
byAccount: string
|
||||
|
||||
|
@ -269,12 +272,10 @@ export class Video implements VideoServerModel {
|
|||
}
|
||||
|
||||
getExactNumberOfViews () {
|
||||
if (this.views < 1000) return ''
|
||||
|
||||
if (this.isLive) {
|
||||
return $localize`${this.views} viewers`
|
||||
return Video.viewersICU({ viewers: this.viewers }, $localize`${this.viewers} viewer(s)`)
|
||||
}
|
||||
|
||||
return $localize`${this.views} views`
|
||||
return Video.viewsICU({ views: this.views }, $localize`{${this.views} view(s)}`)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { forkJoin } from 'rxjs'
|
||||
import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'
|
||||
import { Notifier } from '@app/core'
|
||||
import { prepareIcu } from '@app/helpers'
|
||||
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
|
||||
|
@ -63,9 +64,16 @@ export class UserBanModalComponent extends FormReactive implements OnInit {
|
|||
forkJoin(observables)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
const message = Array.isArray(this.usersToBan)
|
||||
? $localize`${this.usersToBan.length} users banned.`
|
||||
: $localize`User ${this.usersToBan.username} banned.`
|
||||
let message: string
|
||||
|
||||
if (Array.isArray(this.usersToBan)) {
|
||||
message = prepareIcu($localize`{count, plural, =1 {1 user} other {{count} users}} banned.`)(
|
||||
{ count: this.usersToBan.length },
|
||||
$localize`${this.usersToBan.length} users banned.`
|
||||
)
|
||||
} else {
|
||||
message = $localize`User ${this.usersToBan.username} banned.`
|
||||
}
|
||||
|
||||
this.notifier.success(message)
|
||||
|
||||
|
@ -79,7 +87,12 @@ export class UserBanModalComponent extends FormReactive implements OnInit {
|
|||
}
|
||||
|
||||
getModalTitle () {
|
||||
if (Array.isArray(this.usersToBan)) return $localize`Ban ${this.usersToBan.length} users`
|
||||
if (Array.isArray(this.usersToBan)) {
|
||||
return prepareIcu($localize`Ban {count, plural, =1 {1 user} other {{count} users}}`)(
|
||||
{ count: this.usersToBan.length },
|
||||
$localize`Ban ${this.usersToBan.length} users`
|
||||
)
|
||||
}
|
||||
|
||||
return $localize`Ban "${this.usersToBan.username}"`
|
||||
}
|
||||
|
|
|
@ -100,7 +100,8 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
|
|||
return
|
||||
}
|
||||
|
||||
const message = $localize`If you remove user ${user.username}, you won't be able to create another with the same username!`
|
||||
// eslint-disable-next-line max-len
|
||||
const message = $localize`If you remove this user, you won't be able to create another user or channel with <strong>${user.username}</strong> username!`
|
||||
const res = await this.confirmService.confirm(message, $localize`Delete ${user.username}`)
|
||||
if (res === false) return
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'
|
||||
import { Notifier } from '@app/core'
|
||||
import { prepareIcu } from '@app/helpers'
|
||||
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
||||
import { Video } from '@app/shared/shared-main'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
|
@ -80,9 +81,10 @@ export class VideoBlockComponent extends FormReactive implements OnInit {
|
|||
this.videoBlocklistService.blockVideo(options)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
const message = this.isMultiple
|
||||
? $localize`Blocked ${this.videos.length} videos.`
|
||||
: $localize`Blocked ${this.getSingleVideo().name}`
|
||||
const message = prepareIcu($localize`{count, plural, =1 {Blocked {videoName}} other {Blocked {count} videos}}.`)(
|
||||
{ count: this.videos.length, videoName: this.getSingleVideo().name },
|
||||
$localize`Blocked ${this.videos.length} videos.`
|
||||
)
|
||||
|
||||
this.notifier.success(message)
|
||||
this.hide()
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
</my-help>
|
||||
|
||||
<div>
|
||||
<my-select-languages formControlName="videoLanguages"></my-select-languages>
|
||||
<my-select-languages [maxLanguages]="20" formControlName="videoLanguages"></my-select-languages>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -175,7 +175,7 @@ export class VideoMiniatureComponent implements OnInit {
|
|||
|
||||
if (video.scheduledUpdate) {
|
||||
const updateAt = new Date(video.scheduledUpdate.updateAt.toString()).toLocaleString(this.localeId)
|
||||
return $localize`Publication scheduled on ` + updateAt
|
||||
return $localize`Publication scheduled on ${updateAt}`
|
||||
}
|
||||
|
||||
if (video.state.id === VideoState.TRANSCODING_FAILED) {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"moduleResolution": "node",
|
||||
"module": "esnext",
|
||||
"module": "es2020",
|
||||
"experimentalDecorators": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
|
@ -15,11 +15,12 @@
|
|||
"importHelpers": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strictBindCallApply": true,
|
||||
"target": "es2015",
|
||||
"target": "es2017",
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
],
|
||||
"lib": [
|
||||
"ES2020.Intl",
|
||||
"es2018",
|
||||
"es2017",
|
||||
"es2016",
|
||||
|
|
|
@ -1327,6 +1327,45 @@
|
|||
minimatch "^3.0.4"
|
||||
strip-json-comments "^3.1.1"
|
||||
|
||||
"@formatjs/ecma402-abstract@1.11.6":
|
||||
version "1.11.6"
|
||||
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.6.tgz#0e828ddfed6fb3413ae379e48fb7170fb0795db5"
|
||||
integrity sha512-6TcI+IroIK+GTWXBJ643LBJklmCBsqLt1sUTGWfzdBcI5Y6b1L1iamrJB1B5OAQLnhzWveLbmzPYHYsFEZfeig==
|
||||
dependencies:
|
||||
"@formatjs/intl-localematcher" "0.2.27"
|
||||
tslib "2.4.0"
|
||||
|
||||
"@formatjs/fast-memoize@1.2.3":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-1.2.3.tgz#5c950bd64c4959e30bbd16b22a17040fbeb9c4d2"
|
||||
integrity sha512-RVI3e4M7mIxAhKbbyS78H8++fsoiSRZgxh0zReHfvV6p1cpfgG2/k2qJYhJq0RXh6orVtUEsQ3xK9i4tDfsOSg==
|
||||
dependencies:
|
||||
tslib "2.4.0"
|
||||
|
||||
"@formatjs/icu-messageformat-parser@2.1.2":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.2.tgz#9ff4dfc4f1ed613cca2c188b29f299854b86b7f8"
|
||||
integrity sha512-FYQ2pkgbDJxJlst/U5MU2H7+bR9HrZ4x8J4c0etrya24pJzQxYguVlAhc2S6NoEImlQ2LmIIGsURaBQu9bCtew==
|
||||
dependencies:
|
||||
"@formatjs/ecma402-abstract" "1.11.6"
|
||||
"@formatjs/icu-skeleton-parser" "1.3.8"
|
||||
tslib "2.4.0"
|
||||
|
||||
"@formatjs/icu-skeleton-parser@1.3.8":
|
||||
version "1.3.8"
|
||||
resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.8.tgz#3d150fcb45b4867c1db84237ca1f1f701d598918"
|
||||
integrity sha512-CVdsPMs/KvrIDKhMDw8bSq/Zst2bhdn/bTUfVCHi/c/bj462lChIJmW/JP/FaGKgZzdG8slGyVIFLonpG4uqFA==
|
||||
dependencies:
|
||||
"@formatjs/ecma402-abstract" "1.11.6"
|
||||
tslib "2.4.0"
|
||||
|
||||
"@formatjs/intl-localematcher@0.2.27":
|
||||
version "0.2.27"
|
||||
resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.2.27.tgz#8a837ddca17a55d86e4ab68bcbb25b15f547d61d"
|
||||
integrity sha512-XHYcVas2ebDTh3VtfdluvbTjqyMUHqFHARnuJo5KYF/0MKOTmozVSK7PJGnu1IEHdmRdTWuG6TB+2RnkasaxVw==
|
||||
dependencies:
|
||||
tslib "2.4.0"
|
||||
|
||||
"@gar/promisify@^1.0.1":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
|
||||
|
@ -6537,6 +6576,16 @@ interpret@^2.2.0:
|
|||
resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
|
||||
integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==
|
||||
|
||||
intl-messageformat@^10.0.1:
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.0.1.tgz#dae7ae81a477e92ea8691dd73c60d5eb5003f866"
|
||||
integrity sha512-oZWDsNbauuWmPd98+zLEfNojuJkBdVpEWIcWQVCTxSJrhag2/czZnwKBsYa8NcVf4t0fWo0k77v+CBCudKEcjw==
|
||||
dependencies:
|
||||
"@formatjs/ecma402-abstract" "1.11.6"
|
||||
"@formatjs/fast-memoize" "1.2.3"
|
||||
"@formatjs/icu-messageformat-parser" "2.1.2"
|
||||
tslib "2.4.0"
|
||||
|
||||
invert-kv@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
|
||||
|
@ -11055,6 +11104,11 @@ tslib@2.3.1, tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.
|
|||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
|
||||
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
|
||||
|
||||
tslib@2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
|
||||
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
|
||||
|
||||
tslib@^1.8.1, tslib@^1.9.0:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
|
|
Loading…
Reference in New Issue