diff --git a/apps/web/src/components/ui/photo-viewer/LoadingIndicator.tsx b/apps/web/src/components/ui/photo-viewer/LoadingIndicator.tsx
index aedff0c9..2d3aa1d6 100644
--- a/apps/web/src/components/ui/photo-viewer/LoadingIndicator.tsx
+++ b/apps/web/src/components/ui/photo-viewer/LoadingIndicator.tsx
@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'
interface LoadingState {
isVisible: boolean
isConverting: boolean
+ isQueueWaiting: boolean
isHeicFormat: boolean
loadingProgress: number
loadedBytes: number
@@ -34,6 +35,7 @@ const initialLoadingState: LoadingState = {
loadedBytes: 0,
totalBytes: 0,
conversionMessage: undefined,
+ isQueueWaiting: false,
isWebGLLoading: false,
webglMessage: undefined,
@@ -99,7 +101,9 @@ export const LoadingIndicator = ({
// 视频转换状态
<>
- {loadingState.conversionMessage || t('loading.converting')}
+ {loadingState.isQueueWaiting
+ ? loadingState.conversionMessage || t('loading.queue.waiting')
+ : loadingState.conversionMessage || t('loading.converting')}
>
) : loadingState.isWebGLLoading ? (
diff --git a/apps/web/src/lib/image-convert/index.ts b/apps/web/src/lib/image-convert/index.ts
index 1ecbc446..55fa4b1d 100644
--- a/apps/web/src/lib/image-convert/index.ts
+++ b/apps/web/src/lib/image-convert/index.ts
@@ -2,7 +2,12 @@
* 图像转换策略模式实现
* 支持多种浏览器原生不支持的图片格式转换
*/
+import { i18nAtom } from '~/i18n'
+import { jotaiStore } from '~/lib/jotai'
+
import type { LoadingCallbacks } from '../image-loader-manager'
+import type { PipelineOptions } from './pipeline'
+import { ImageConversionPipeline } from './pipeline'
import { HeicConverterStrategy } from './strategies/heic'
import { TiffConverterStrategy } from './strategies/tiff'
import type { ConversionResult, ImageConverterStrategy } from './type'
@@ -10,8 +15,16 @@ import type { ConversionResult, ImageConverterStrategy } from './type'
// 图像转换策略管理器
export class ImageConverterManager {
private strategies = new Map()
+ private readonly conversionPipeline: ImageConversionPipeline
+ private readonly pendingConversions = new Map<
+ string,
+ Promise
+ >()
- constructor() {
+ constructor(options: PipelineOptions = {}) {
+ this.conversionPipeline = new ImageConversionPipeline({
+ maxConcurrent: options.maxConcurrent ?? 2,
+ })
// 注册默认策略
this.registerStrategy(new HeicConverterStrategy())
this.registerStrategy(new TiffConverterStrategy())
@@ -120,7 +133,44 @@ export class ImageConverterManager {
}
console.info(`Converting image using ${strategy.getName()} strategy`)
- return await strategy.convert(blob, originalUrl, callbacks)
+ const taskKey = this.getConversionTaskKey(strategy, originalUrl)
+
+ const onLoadingStateUpdate = callbacks?.onLoadingStateUpdate
+ const pipelineActive = this.conversionPipeline.getActiveCount()
+ const maxConcurrent = this.conversionPipeline.getMaxConcurrent()
+ const isPipelineSaturated = pipelineActive >= maxConcurrent
+
+ const existingTask = this.pendingConversions.get(taskKey)
+ if (existingTask) {
+ console.info(
+ `Joining pending conversion task for ${strategy.getName()} (${originalUrl})`,
+ )
+ return await existingTask
+ }
+
+ if (onLoadingStateUpdate && isPipelineSaturated) {
+ const i18n = jotaiStore.get(i18nAtom)
+ onLoadingStateUpdate({
+ isConverting: true,
+ isQueueWaiting: true,
+ conversionMessage: i18n.t('loading.queue.waiting'),
+ })
+ }
+
+ const conversionPromise = this.conversionPipeline.enqueue(async () => {
+ try {
+ onLoadingStateUpdate?.({
+ isQueueWaiting: false,
+ conversionMessage: undefined,
+ })
+ return await strategy.convert(blob, originalUrl, callbacks)
+ } finally {
+ this.pendingConversions.delete(taskKey)
+ }
+ })
+
+ this.pendingConversions.set(taskKey, conversionPromise)
+ return await conversionPromise
}
/**
@@ -129,6 +179,30 @@ export class ImageConverterManager {
getSupportedFormats(): string[] {
return Array.from(this.strategies.keys())
}
+
+ getPipelineStats(): {
+ active: number
+ pending: number
+ } {
+ return {
+ active: this.conversionPipeline.getActiveCount(),
+ pending: this.conversionPipeline.getPendingCount(),
+ }
+ }
+
+ /**
+ * 调整管道的最大并发转换数量
+ */
+ setMaxConcurrentConversions(maxConcurrent: number): void {
+ this.conversionPipeline.setMaxConcurrent(maxConcurrent)
+ }
+
+ private getConversionTaskKey(
+ strategy: ImageConverterStrategy,
+ originalUrl: string,
+ ): string {
+ return `${strategy.getName()}::${originalUrl}`
+ }
}
// 导出单例实例
diff --git a/apps/web/src/lib/image-convert/pipeline.ts b/apps/web/src/lib/image-convert/pipeline.ts
new file mode 100644
index 00000000..76ea9fc2
--- /dev/null
+++ b/apps/web/src/lib/image-convert/pipeline.ts
@@ -0,0 +1,90 @@
+export interface PipelineOptions {
+ maxConcurrent?: number
+}
+
+type TaskExecutor = () => Promise
+
+type QueueTask = {
+ execute: TaskExecutor
+ resolve: (value: T) => void
+ reject: (reason?: unknown) => void
+}
+
+/**
+ * Lightweight promise queue to throttle image conversion workloads
+ */
+export class ImageConversionPipeline {
+ private maxConcurrent: number
+ private readonly queue: Array> = []
+ private activeCount = 0
+
+ constructor(options: PipelineOptions = {}) {
+ const { maxConcurrent = 2 } = options
+ this.maxConcurrent = Math.max(1, maxConcurrent)
+ }
+
+ enqueue(task: TaskExecutor): Promise {
+ return new Promise((resolve, reject) => {
+ const queueTask: QueueTask = {
+ execute: task,
+ resolve,
+ reject,
+ }
+
+ this.queue.push(queueTask)
+ this.drainQueue()
+ })
+ }
+
+ getActiveCount(): number {
+ return this.activeCount
+ }
+
+ getPendingCount(): number {
+ return this.queue.length
+ }
+
+ getMaxConcurrent(): number {
+ return this.maxConcurrent
+ }
+
+ setMaxConcurrent(maxConcurrent: number): void {
+ if (!Number.isFinite(maxConcurrent)) {
+ throw new TypeError('maxConcurrent must be a finite number')
+ }
+
+ const normalizedValue = Math.max(1, Math.floor(maxConcurrent))
+ if (normalizedValue === this.maxConcurrent) {
+ return
+ }
+
+ this.maxConcurrent = normalizedValue
+ this.drainQueue()
+ }
+
+ private drainQueue(): void {
+ if (this.activeCount >= this.maxConcurrent) {
+ return
+ }
+
+ const nextTask = this.queue.shift()
+ if (!nextTask) {
+ return
+ }
+
+ this.activeCount += 1
+
+ // Run task asynchronously without blocking
+ ;(async () => {
+ try {
+ const result = await nextTask.execute()
+ nextTask.resolve(result)
+ } catch (error) {
+ nextTask.reject(error)
+ } finally {
+ this.activeCount = Math.max(0, this.activeCount - 1)
+ this.drainQueue()
+ }
+ })()
+ }
+}
diff --git a/apps/web/src/lib/image-convert/strategies/heic.ts b/apps/web/src/lib/image-convert/strategies/heic.ts
index 868ce2a8..8a526528 100644
--- a/apps/web/src/lib/image-convert/strategies/heic.ts
+++ b/apps/web/src/lib/image-convert/strategies/heic.ts
@@ -42,6 +42,7 @@ export class HeicConverterStrategy implements ImageConverterStrategy {
// 更新转换状态
onLoadingStateUpdate?.({
isConverting: true,
+ isQueueWaiting: false,
conversionMessage: i18n.t('loading.heic.converting'),
isHeicFormat: true,
loadingProgress: 100,
diff --git a/apps/web/src/lib/image-convert/strategies/tiff.ts b/apps/web/src/lib/image-convert/strategies/tiff.ts
index 526daf4e..267dad44 100644
--- a/apps/web/src/lib/image-convert/strategies/tiff.ts
+++ b/apps/web/src/lib/image-convert/strategies/tiff.ts
@@ -28,6 +28,7 @@ export class TiffConverterStrategy implements ImageConverterStrategy {
// 更新转换状态
onLoadingStateUpdate?.({
isConverting: true,
+ isQueueWaiting: false,
conversionMessage: 'Converting TIFF image...',
})
@@ -48,7 +49,7 @@ export class TiffConverterStrategy implements ImageConverterStrategy {
// 浏览器支持检测
private isBrowserSupportTiff(): boolean {
- // safari 支持tiff
+ // safari 支持 tiff
if (isSafari) {
return true
}
@@ -147,7 +148,7 @@ export class TiffConverterStrategy implements ImageConverterStrategy {
break
}
case 16: {
- // 16位数据,需要转换为8位
+ // 16 位数据,需要转换为 8 位
const data = sourceData as Uint16Array
targetData[dstIndex] = Math.round((data[srcIndex] || 0) / 257) // R
targetData[dstIndex + 1] =
@@ -165,7 +166,7 @@ export class TiffConverterStrategy implements ImageConverterStrategy {
break
}
case 32: {
- // 32位浮点数据
+ // 32 位浮点数据
const data = sourceData as Float32Array | Float64Array
targetData[dstIndex] = Math.round((data[srcIndex] || 0) * 255) // R
targetData[dstIndex + 1] =
diff --git a/apps/web/src/lib/image-loader-manager.ts b/apps/web/src/lib/image-loader-manager.ts
index e4099c3f..8525f73c 100644
--- a/apps/web/src/lib/image-loader-manager.ts
+++ b/apps/web/src/lib/image-loader-manager.ts
@@ -13,6 +13,7 @@ export interface LoadingState {
loadedBytes?: number
totalBytes?: number
isConverting?: boolean
+ isQueueWaiting?: boolean
conversionMessage?: string
codecInfo?: string
}
diff --git a/locales/app/en.json b/locales/app/en.json
index 4f946d38..600f2a99 100644
--- a/locales/app/en.json
+++ b/locales/app/en.json
@@ -316,6 +316,7 @@
"loading.default": "Loading",
"loading.heic.converting": "Converting HEIC/HEIF image format...",
"loading.heic.main": "HEIC",
+ "loading.queue.waiting": "Waiting for available converter...",
"loading.webgl.building": "Building high-quality textures...",
"loading.webgl.main": "WebGL Texture Loading",
"minimap.loading": "Loading map...",
@@ -377,4 +378,4 @@
"video.conversion.webcodecs.not.supported": "WebCodecs is not supported in this browser",
"video.format.mov.not.supported": "Browser does not support MOV format, conversion required",
"video.format.mov.supported": "Browser natively supports MOV format, skipping conversion"
-}
+}
\ No newline at end of file
diff --git a/locales/app/jp.json b/locales/app/jp.json
index 582620ba..feeb03b2 100644
--- a/locales/app/jp.json
+++ b/locales/app/jp.json
@@ -41,37 +41,37 @@
"action.view.layout": "レイアウト",
"action.view.settings": "ビュー設定",
"action.view.title": "ビュー",
- "date.day.1": "1日",
- "date.day.10": "10日",
- "date.day.11": "11日",
- "date.day.12": "12日",
- "date.day.13": "13日",
- "date.day.14": "14日",
- "date.day.15": "15日",
- "date.day.16": "16日",
- "date.day.17": "17日",
- "date.day.18": "18日",
- "date.day.19": "19日",
- "date.day.2": "2日",
- "date.day.20": "20日",
- "date.day.21": "21日",
- "date.day.22": "22日",
- "date.day.23": "23日",
- "date.day.24": "24日",
- "date.day.25": "25日",
- "date.day.26": "26日",
- "date.day.27": "27日",
- "date.day.28": "28日",
- "date.day.29": "29日",
- "date.day.3": "3日",
- "date.day.30": "30日",
- "date.day.31": "31日",
- "date.day.4": "4日",
- "date.day.5": "5日",
- "date.day.6": "6日",
- "date.day.7": "7日",
- "date.day.8": "8日",
- "date.day.9": "9日",
+ "date.day.1": "1 日",
+ "date.day.10": "10 日",
+ "date.day.11": "11 日",
+ "date.day.12": "12 日",
+ "date.day.13": "13 日",
+ "date.day.14": "14 日",
+ "date.day.15": "15 日",
+ "date.day.16": "16 日",
+ "date.day.17": "17 日",
+ "date.day.18": "18 日",
+ "date.day.19": "19 日",
+ "date.day.2": "2 日",
+ "date.day.20": "20 日",
+ "date.day.21": "21 日",
+ "date.day.22": "22 日",
+ "date.day.23": "23 日",
+ "date.day.24": "24 日",
+ "date.day.25": "25 日",
+ "date.day.26": "26 日",
+ "date.day.27": "27 日",
+ "date.day.28": "28 日",
+ "date.day.29": "29 日",
+ "date.day.3": "3 日",
+ "date.day.30": "30 日",
+ "date.day.31": "31 日",
+ "date.day.4": "4 日",
+ "date.day.5": "5 日",
+ "date.day.6": "6 日",
+ "date.day.7": "7 日",
+ "date.day.8": "8 日",
+ "date.day.9": "9 日",
"date.month.1": "1月",
"date.month.10": "10月",
"date.month.11": "11月",
@@ -312,6 +312,7 @@
"loading.default": "読み込み中",
"loading.heic.converting": "HEIC/HEIF 画像フォーマットを変換中...",
"loading.heic.main": "HEIC",
+ "loading.queue.waiting": "変換待機中です...",
"loading.webgl.building": "高品質テクスチャを構築中...",
"loading.webgl.main": "WebGL テクスチャの読み込み",
"minimap.loading": "地図を読み込み中...",
@@ -370,4 +371,4 @@
"video.conversion.webcodecs.not.supported": "このブラウザは WebCodecs をサポートしていません",
"video.format.mov.not.supported": "ブラウザが MOV 形式をサポートしていないため、変換が必要です",
"video.format.mov.supported": "ブラウザが MOV 形式をネイティブでサポートしているため、変換をスキップします"
-}
+}
\ No newline at end of file
diff --git a/locales/app/ko.json b/locales/app/ko.json
index 48c38af4..959e8acd 100644
--- a/locales/app/ko.json
+++ b/locales/app/ko.json
@@ -41,37 +41,37 @@
"action.view.layout": "레이아웃",
"action.view.settings": "보기 설정",
"action.view.title": "보기",
- "date.day.1": "1일",
- "date.day.10": "10일",
- "date.day.11": "11일",
- "date.day.12": "12일",
- "date.day.13": "13일",
- "date.day.14": "14일",
- "date.day.15": "15일",
- "date.day.16": "16일",
- "date.day.17": "17일",
- "date.day.18": "18일",
- "date.day.19": "19일",
- "date.day.2": "2일",
- "date.day.20": "20일",
- "date.day.21": "21일",
- "date.day.22": "22일",
- "date.day.23": "23일",
- "date.day.24": "24일",
- "date.day.25": "25일",
- "date.day.26": "26일",
- "date.day.27": "27일",
- "date.day.28": "28일",
- "date.day.29": "29일",
- "date.day.3": "3일",
- "date.day.30": "30일",
- "date.day.31": "31일",
- "date.day.4": "4일",
- "date.day.5": "5일",
- "date.day.6": "6일",
- "date.day.7": "7일",
- "date.day.8": "8일",
- "date.day.9": "9일",
+ "date.day.1": "1 일",
+ "date.day.10": "10 일",
+ "date.day.11": "11 일",
+ "date.day.12": "12 일",
+ "date.day.13": "13 일",
+ "date.day.14": "14 일",
+ "date.day.15": "15 일",
+ "date.day.16": "16 일",
+ "date.day.17": "17 일",
+ "date.day.18": "18 일",
+ "date.day.19": "19 일",
+ "date.day.2": "2 일",
+ "date.day.20": "20 일",
+ "date.day.21": "21 일",
+ "date.day.22": "22 일",
+ "date.day.23": "23 일",
+ "date.day.24": "24 일",
+ "date.day.25": "25 일",
+ "date.day.26": "26 일",
+ "date.day.27": "27 일",
+ "date.day.28": "28 일",
+ "date.day.29": "29 일",
+ "date.day.3": "3 일",
+ "date.day.30": "30 일",
+ "date.day.31": "31 일",
+ "date.day.4": "4 일",
+ "date.day.5": "5 일",
+ "date.day.6": "6 일",
+ "date.day.7": "7 일",
+ "date.day.8": "8 일",
+ "date.day.9": "9 일",
"date.month.1": "1월",
"date.month.10": "10월",
"date.month.11": "11월",
@@ -312,6 +312,7 @@
"loading.default": "로딩 중",
"loading.heic.converting": "HEIC/HEIF 이미지 형식 변환 중...",
"loading.heic.main": "HEIC",
+ "loading.queue.waiting": "변환 대기 중입니다...",
"loading.webgl.building": "고품질 텍스처 구축 중...",
"loading.webgl.main": "WebGL 텍스처 로딩",
"minimap.loading": "지도 로딩 중...",
@@ -370,4 +371,4 @@
"video.conversion.webcodecs.not.supported": "이 브라우저는 WebCodecs 를 지원하지 않습니다",
"video.format.mov.not.supported": "브라우저가 MOV 형식을 지원하지 않아 변환이 필요합니다.",
"video.format.mov.supported": "브라우저가 MOV 형식을 기본적으로 지원하므로 변환을 건너뜁니다."
-}
+}
\ No newline at end of file
diff --git a/locales/app/zh-CN.json b/locales/app/zh-CN.json
index 736975a3..78affb41 100644
--- a/locales/app/zh-CN.json
+++ b/locales/app/zh-CN.json
@@ -313,6 +313,7 @@
"loading.default": "加载中",
"loading.heic.converting": "正在转换 HEIC/HEIF 图像格式...",
"loading.heic.main": "HEIC",
+ "loading.queue.waiting": "正在排队等待转换...",
"loading.webgl.building": "正在构建高质量纹理...",
"loading.webgl.main": "WebGL 纹理加载",
"minimap.loading": "加载地图中...",
@@ -374,4 +375,4 @@
"video.conversion.webcodecs.not.supported": "此浏览器不支持 WebCodecs",
"video.format.mov.not.supported": "浏览器不支持 MOV 格式,需要转换",
"video.format.mov.supported": "浏览器原生支持 MOV 格式,跳过转换"
-}
+}
\ No newline at end of file
diff --git a/locales/app/zh-HK.json b/locales/app/zh-HK.json
index dc6ef7e6..9d992fca 100644
--- a/locales/app/zh-HK.json
+++ b/locales/app/zh-HK.json
@@ -41,37 +41,37 @@
"action.view.layout": "佈局",
"action.view.settings": "檢視設定",
"action.view.title": "檢視",
- "date.day.1": "1日",
- "date.day.10": "10日",
- "date.day.11": "11日",
- "date.day.12": "12日",
- "date.day.13": "13日",
- "date.day.14": "14日",
- "date.day.15": "15日",
- "date.day.16": "16日",
- "date.day.17": "17日",
- "date.day.18": "18日",
- "date.day.19": "19日",
- "date.day.2": "2日",
- "date.day.20": "20日",
- "date.day.21": "21日",
- "date.day.22": "22日",
- "date.day.23": "23日",
- "date.day.24": "24日",
- "date.day.25": "25日",
- "date.day.26": "26日",
- "date.day.27": "27日",
- "date.day.28": "28日",
- "date.day.29": "29日",
- "date.day.3": "3日",
- "date.day.30": "30日",
- "date.day.31": "31日",
- "date.day.4": "4日",
- "date.day.5": "5日",
- "date.day.6": "6日",
- "date.day.7": "7日",
- "date.day.8": "8日",
- "date.day.9": "9日",
+ "date.day.1": "1 日",
+ "date.day.10": "10 日",
+ "date.day.11": "11 日",
+ "date.day.12": "12 日",
+ "date.day.13": "13 日",
+ "date.day.14": "14 日",
+ "date.day.15": "15 日",
+ "date.day.16": "16 日",
+ "date.day.17": "17 日",
+ "date.day.18": "18 日",
+ "date.day.19": "19 日",
+ "date.day.2": "2 日",
+ "date.day.20": "20 日",
+ "date.day.21": "21 日",
+ "date.day.22": "22 日",
+ "date.day.23": "23 日",
+ "date.day.24": "24 日",
+ "date.day.25": "25 日",
+ "date.day.26": "26 日",
+ "date.day.27": "27 日",
+ "date.day.28": "28 日",
+ "date.day.29": "29 日",
+ "date.day.3": "3 日",
+ "date.day.30": "30 日",
+ "date.day.31": "31 日",
+ "date.day.4": "4 日",
+ "date.day.5": "5 日",
+ "date.day.6": "6 日",
+ "date.day.7": "7 日",
+ "date.day.8": "8 日",
+ "date.day.9": "9 日",
"date.month.1": "1月",
"date.month.10": "7月",
"date.month.11": "8月",
@@ -312,6 +312,7 @@
"loading.default": "載入中",
"loading.heic.converting": "正在轉換 HEIC/HEIF 圖像格式...",
"loading.heic.main": "HEIC",
+ "loading.queue.waiting": "正在排隊等候轉換...",
"loading.webgl.building": "正在建置高品質紋理...",
"loading.webgl.main": "WebGL 紋理載入",
"minimap.loading": "載入地圖中...",
@@ -370,4 +371,4 @@
"video.conversion.webcodecs.not.supported": "此瀏覽器不支援 WebCodecs",
"video.format.mov.not.supported": "瀏覽器不支援 MOV 格式,需要轉換",
"video.format.mov.supported": "瀏覽器原生支援 MOV 格式,跳過轉換"
-}
+}
\ No newline at end of file
diff --git a/locales/app/zh-TW.json b/locales/app/zh-TW.json
index c67b6920..2fc4110d 100644
--- a/locales/app/zh-TW.json
+++ b/locales/app/zh-TW.json
@@ -41,37 +41,37 @@
"action.view.layout": "佈局",
"action.view.settings": "檢視設定",
"action.view.title": "檢視",
- "date.day.1": "1日",
- "date.day.10": "10日",
- "date.day.11": "11日",
- "date.day.12": "12日",
- "date.day.13": "13日",
- "date.day.14": "14日",
- "date.day.15": "15日",
- "date.day.16": "16日",
- "date.day.17": "17日",
- "date.day.18": "18日",
- "date.day.19": "19日",
- "date.day.2": "2日",
- "date.day.20": "20日",
- "date.day.21": "21日",
- "date.day.22": "22日",
- "date.day.23": "23日",
- "date.day.24": "24日",
- "date.day.25": "25日",
- "date.day.26": "26日",
- "date.day.27": "27日",
- "date.day.28": "28日",
- "date.day.29": "29日",
- "date.day.3": "3日",
- "date.day.30": "30日",
- "date.day.31": "31日",
- "date.day.4": "4日",
- "date.day.5": "5日",
- "date.day.6": "6日",
- "date.day.7": "7日",
- "date.day.8": "8日",
- "date.day.9": "9日",
+ "date.day.1": "1 日",
+ "date.day.10": "10 日",
+ "date.day.11": "11 日",
+ "date.day.12": "12 日",
+ "date.day.13": "13 日",
+ "date.day.14": "14 日",
+ "date.day.15": "15 日",
+ "date.day.16": "16 日",
+ "date.day.17": "17 日",
+ "date.day.18": "18 日",
+ "date.day.19": "19 日",
+ "date.day.2": "2 日",
+ "date.day.20": "20 日",
+ "date.day.21": "21 日",
+ "date.day.22": "22 日",
+ "date.day.23": "23 日",
+ "date.day.24": "24 日",
+ "date.day.25": "25 日",
+ "date.day.26": "26 日",
+ "date.day.27": "27 日",
+ "date.day.28": "28 日",
+ "date.day.29": "29 日",
+ "date.day.3": "3 日",
+ "date.day.30": "30 日",
+ "date.day.31": "31 日",
+ "date.day.4": "4 日",
+ "date.day.5": "5 日",
+ "date.day.6": "6 日",
+ "date.day.7": "7 日",
+ "date.day.8": "8 日",
+ "date.day.9": "9 日",
"date.month.1": "1月",
"date.month.10": "10月",
"date.month.11": "11月",
@@ -311,6 +311,7 @@
"loading.default": "載入中",
"loading.heic.converting": "正在轉換 HEIC/HEIF 圖像格式...",
"loading.heic.main": "HEIC",
+ "loading.queue.waiting": "正在排隊等待轉換...",
"loading.webgl.building": "正在建置高品質紋理...",
"loading.webgl.main": "WebGL 紋理載入",
"minimap.loading": "載入地圖中...",
@@ -369,4 +370,4 @@
"video.conversion.webcodecs.not.supported": "此瀏覽器不支援 WebCodecs",
"video.format.mov.not.supported": "瀏覽器不支援 MOV 格式,需要轉換",
"video.format.mov.supported": "瀏覽器原生支援 MOV 格式,跳過轉換"
-}
+}
\ No newline at end of file