Files
dify/web/utils/format.ts
qiuqiua 9ef6b90843 feat: sync main branch (#31938)
Signed-off-by: majiayu000 <1835304752@qq.com>
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: NeatGuyCoding <15627489+NeatGuyCoding@users.noreply.github.com>
Signed-off-by: -LAN- <laipz8200@outlook.com>
Signed-off-by: yihong0618 <zouzou0208@gmail.com>
Co-authored-by: QuantumGhost <obelisk.reg+git@gmail.com>
Co-authored-by: 盐粒 Yanli <yanli@dify.ai>
Co-authored-by: wangxiaolei <fatelei@gmail.com>
Co-authored-by: Stephen Zhou <38493346+hyoban@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Cursx <33718736+Cursx@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: lif <1835304752@qq.com>
Co-authored-by: 非法操作 <hjlarry@163.com>
Co-authored-by: Asuka Minato <i@asukaminato.eu.org>
Co-authored-by: fenglin <790872612@qq.com>
Co-authored-by: qiaofenglin <qiaofenglin@baidu.com>
Co-authored-by: -LAN- <laipz8200@outlook.com>
Co-authored-by: TomoOkuyama <49631611+TomoOkuyama@users.noreply.github.com>
Co-authored-by: Tomo Okuyama <tomo.okuyama@intersystems.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: zyssyz123 <916125788@qq.com>
Co-authored-by: hj24 <mambahj24@gmail.com>
Co-authored-by: Coding On Star <447357187@qq.com>
Co-authored-by: CodingOnStar <hanxujiang@dify.ai>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
Co-authored-by: Xiangxuan Qu <fghpdf@outlook.com>
Co-authored-by: fghpdf <fghpdf@users.noreply.github.com>
Co-authored-by: coopercoder <whitetiger0127@163.com>
Co-authored-by: zhaiguangpeng <zhaiguangpeng@didiglobal.com>
Co-authored-by: Junyan Qin (Chin) <rockchinq@gmail.com>
Co-authored-by: E.G <146701565+GlobalStar117@users.noreply.github.com>
Co-authored-by: GlobalStar117 <GlobalStar117@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Co-authored-by: CodingOnStar <hanxujiang@dify.com>
Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
Co-authored-by: heyszt <270985384@qq.com>
Co-authored-by: NeatGuyCoding <15627489+NeatGuyCoding@users.noreply.github.com>
Co-authored-by: Yeuoly <45712896+Yeuoly@users.noreply.github.com>
Co-authored-by: zxhlyh <jasonapring2015@outlook.com>
Co-authored-by: moonpanda <chuanzegao@163.com>
Co-authored-by: warlocgao <warlocgao@tencent.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: KVOJJJin <jzongcode@gmail.com>
Co-authored-by: eux <euxx@users.noreply.github.com>
Co-authored-by: bangjiehan <bangjiehan@gmail.com>
Co-authored-by: FFXN <31929997+FFXN@users.noreply.github.com>
Co-authored-by: Jyong <76649700+JohnJyong@users.noreply.github.com>
Co-authored-by: Nie Ronghua <nieronghua@sf-express.com>
Co-authored-by: JQSevenMiao <141806521+JQSevenMiao@users.noreply.github.com>
Co-authored-by: jiasiqi <jiasiqi3@tal.com>
Co-authored-by: Seokrin Taron Sung <sungsjade@gmail.com>
Co-authored-by: CrabSAMA <40541269+CrabSAMA@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: yihong <zouzou0208@gmail.com>
Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: Wu Tianwei <30284043+WTW0313@users.noreply.github.com>
Co-authored-by: yessenia <yessenia.contact@gmail.com>
Co-authored-by: Jax <anobaka@qq.com>
Co-authored-by: niveshdandyan <155956228+niveshdandyan@users.noreply.github.com>
Co-authored-by: OSS Contributor <oss-contributor@example.com>
Co-authored-by: niveshdandyan <niveshdandyan@users.noreply.github.com>
Co-authored-by: Sean Kenneth Doherty <Smaster7772@gmail.com>
2026-02-04 19:04:24 +08:00

151 lines
4.2 KiB
TypeScript

import type { Dayjs } from 'dayjs'
import type { Locale } from '@/i18n-config'
import { localeMap } from '@/i18n-config/language'
import 'dayjs/locale/de'
import 'dayjs/locale/es'
import 'dayjs/locale/fa'
import 'dayjs/locale/fr'
import 'dayjs/locale/hi'
import 'dayjs/locale/id'
import 'dayjs/locale/it'
import 'dayjs/locale/ja'
import 'dayjs/locale/ko'
import 'dayjs/locale/pl'
import 'dayjs/locale/pt-br'
import 'dayjs/locale/ro'
import 'dayjs/locale/ru'
import 'dayjs/locale/sl'
import 'dayjs/locale/th'
import 'dayjs/locale/tr'
import 'dayjs/locale/uk'
import 'dayjs/locale/vi'
import 'dayjs/locale/zh-cn'
import 'dayjs/locale/zh-tw'
/**
* Formats a number with comma separators.
* @example formatNumber(1234567) will return '1,234,567'
* @example formatNumber(1234567.89) will return '1,234,567.89'
* @example formatNumber(0.0000008) will return '0.0000008'
*/
export const formatNumber = (num: number | string) => {
if (!num)
return num
const n = typeof num === 'string' ? Number(num) : num
let numStr: string
// Force fixed decimal for small numbers to avoid scientific notation
if (Math.abs(n) < 0.001 && n !== 0) {
const str = n.toString()
const match = str.match(/e-(\d+)$/)
let precision: number
if (match) {
// Scientific notation: precision is exponent + decimal digits in mantissa
const exponent = Number.parseInt(match[1], 10)
const mantissa = str.split('e')[0]
const mantissaDecimalPart = mantissa.split('.')[1]
precision = exponent + (mantissaDecimalPart?.length || 0)
}
else {
// Decimal notation: count decimal places
const decimalPart = str.split('.')[1]
precision = decimalPart?.length || 0
}
numStr = n.toFixed(precision)
}
else {
numStr = n.toString()
}
const parts = numStr.split('.')
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',')
return parts.join('.')
}
/**
* Format file size into standard string format.
* @param fileSize file size (Byte)
* @example formatFileSize(1024) will return '1.00 KB'
* @example formatFileSize(1024 * 1024) will return '1.00 MB'
*/
export const formatFileSize = (fileSize: number) => {
if (!fileSize)
return fileSize
const units = ['', 'K', 'M', 'G', 'T', 'P']
let index = 0
while (fileSize >= 1024 && index < units.length) {
fileSize = fileSize / 1024
index++
}
if (index === 0)
return `${fileSize.toFixed(2)} bytes`
return `${fileSize.toFixed(2)} ${units[index]}B`
}
/**
* Format time into standard string format.
* @example formatTime(60) will return '1.00 min'
* @example formatTime(60 * 60) will return '1.00 h'
*/
export const formatTime = (seconds: number) => {
if (!seconds)
return seconds
const units = ['sec', 'min', 'h']
let index = 0
while (seconds >= 60 && index < units.length) {
seconds = seconds / 60
index++
}
return `${seconds.toFixed(2)} ${units[index]}`
}
/**
* Formats a number into a readable string using "k", "M", or "B" suffix.
* @example
* 950 => "950"
* 1200 => "1.2k"
* 1500000 => "1.5M"
* 2000000000 => "2B"
*
* @param {number} num - The number to format
* @returns {string} - The formatted number string
*/
export const formatNumberAbbreviated = (num: number) => {
// If less than 1000, return as-is
if (num < 1000)
return num.toString()
// Define thresholds and suffixes
const units = [
{ value: 1e9, symbol: 'B' },
{ value: 1e6, symbol: 'M' },
{ value: 1e3, symbol: 'k' },
]
for (let i = 0; i < units.length; i++) {
if (num >= units[i].value) {
const value = num / units[i].value
let rounded = Math.round(value * 10) / 10
let unitIndex = i
// If rounded value >= 1000, promote to next unit
if (rounded >= 1000 && i > 0) {
rounded = rounded / 1000
unitIndex = i - 1
}
const formatted = rounded.toFixed(1)
return formatted.endsWith('.0')
? `${Number.parseInt(formatted)}${units[unitIndex].symbol}`
: `${formatted}${units[unitIndex].symbol}`
}
}
// Fallback: if no threshold matched, return the number string
return num.toString()
}
export const formatToLocalTime = (time: Dayjs, local: Locale, format: string) => {
return time.locale(localeMap[local] ?? 'en').format(format)
}