mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-08 22:21:23 +00:00
Co-authored-by: Daniel <57051444+daniel-lxs@users.noreply.github.com> Co-authored-by: Daniel Riccio <ricciodaniel98@gmail.com>
This commit is contained in:
parent
5f29348519
commit
bde7a7bf2a
24 changed files with 615 additions and 10 deletions
|
|
@ -18,6 +18,20 @@ import { Mode } from "./modes"
|
|||
import { RouterModels } from "./api"
|
||||
import { MarketplaceItem } from "../services/marketplace/types"
|
||||
|
||||
// Indexing status types
|
||||
export interface IndexingStatus {
|
||||
systemStatus: string
|
||||
message?: string
|
||||
processedItems: number
|
||||
totalItems: number
|
||||
currentItemUnit?: string
|
||||
}
|
||||
|
||||
export interface IndexingStatusUpdateMessage {
|
||||
type: "indexingStatusUpdate"
|
||||
values: IndexingStatus
|
||||
}
|
||||
|
||||
export interface LanguageModelChatSelector {
|
||||
vendor?: string
|
||||
family?: string
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import { MAX_IMAGES_PER_MESSAGE } from "./ChatView"
|
|||
import ContextMenu from "./ContextMenu"
|
||||
import { VolumeX, Pin, Check } from "lucide-react"
|
||||
import { IconButton } from "./IconButton"
|
||||
import { IndexingStatusDot } from "./IndexingStatusBadge"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { usePromptHistory } from "./hooks/usePromptHistory"
|
||||
|
||||
|
|
@ -78,6 +79,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
togglePinnedApiConfig,
|
||||
taskHistory,
|
||||
clineMessages,
|
||||
codebaseIndexConfig,
|
||||
} = useExtensionState()
|
||||
|
||||
// Find the ID and display text for the currently selected API configuration
|
||||
|
|
@ -1171,6 +1173,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
</div>
|
||||
|
||||
<div className={cn("flex", "items-center", "gap-0.5", "shrink-0")}>
|
||||
{codebaseIndexConfig?.codebaseIndexEnabled && <IndexingStatusDot />}
|
||||
<IconButton
|
||||
iconClass={isEnhancingPrompt ? "codicon-loading" : "codicon-sparkle"}
|
||||
title={t("chat:enhancePrompt")}
|
||||
|
|
|
|||
158
webview-ui/src/components/chat/IndexingStatusBadge.tsx
Normal file
158
webview-ui/src/components/chat/IndexingStatusBadge.tsx
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
import React, { useState, useEffect, useMemo } from "react"
|
||||
import { cn } from "@src/lib/utils"
|
||||
import { vscode } from "@src/utils/vscode"
|
||||
import { useAppTranslation } from "@/i18n/TranslationContext"
|
||||
import { useTooltip } from "@/hooks/useTooltip"
|
||||
import type { IndexingStatus, IndexingStatusUpdateMessage } from "@roo/ExtensionMessage"
|
||||
|
||||
interface IndexingStatusDotProps {
|
||||
className?: string
|
||||
}
|
||||
|
||||
export const IndexingStatusDot: React.FC<IndexingStatusDotProps> = ({ className }) => {
|
||||
const { t } = useAppTranslation()
|
||||
const { showTooltip, handleMouseEnter, handleMouseLeave, cleanup } = useTooltip({ delay: 300 })
|
||||
const [isHovered, setIsHovered] = useState(false)
|
||||
|
||||
const [indexingStatus, setIndexingStatus] = useState<IndexingStatus>({
|
||||
systemStatus: "Standby",
|
||||
processedItems: 0,
|
||||
totalItems: 0,
|
||||
currentItemUnit: "items",
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
// Request initial indexing status
|
||||
vscode.postMessage({ type: "requestIndexingStatus" })
|
||||
|
||||
// Set up message listener for status updates
|
||||
const handleMessage = (event: MessageEvent<IndexingStatusUpdateMessage>) => {
|
||||
if (event.data.type === "indexingStatusUpdate") {
|
||||
const status = event.data.values
|
||||
setIndexingStatus(status)
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("message", handleMessage)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("message", handleMessage)
|
||||
cleanup()
|
||||
}
|
||||
}, [cleanup])
|
||||
|
||||
// Calculate progress percentage with memoization
|
||||
const progressPercentage = useMemo(
|
||||
() =>
|
||||
indexingStatus.totalItems > 0
|
||||
? Math.round((indexingStatus.processedItems / indexingStatus.totalItems) * 100)
|
||||
: 0,
|
||||
[indexingStatus.processedItems, indexingStatus.totalItems],
|
||||
)
|
||||
|
||||
// Get tooltip text with internationalization
|
||||
const getTooltipText = () => {
|
||||
switch (indexingStatus.systemStatus) {
|
||||
case "Standby":
|
||||
return t("chat:indexingStatus.ready")
|
||||
case "Indexing":
|
||||
return t("chat:indexingStatus.indexing", { percentage: progressPercentage })
|
||||
case "Indexed":
|
||||
return t("chat:indexingStatus.indexed")
|
||||
case "Error":
|
||||
return t("chat:indexingStatus.error")
|
||||
default:
|
||||
return t("chat:indexingStatus.status")
|
||||
}
|
||||
}
|
||||
|
||||
// Navigate to settings when clicked
|
||||
const handleClick = () => {
|
||||
window.postMessage(
|
||||
{
|
||||
type: "action",
|
||||
action: "settingsButtonClicked",
|
||||
values: { section: "experimental" },
|
||||
},
|
||||
"*",
|
||||
)
|
||||
}
|
||||
|
||||
const handleMouseEnterButton = () => {
|
||||
setIsHovered(true)
|
||||
handleMouseEnter()
|
||||
}
|
||||
|
||||
const handleMouseLeaveButton = () => {
|
||||
setIsHovered(false)
|
||||
handleMouseLeave()
|
||||
}
|
||||
|
||||
// Get status color classes based on status and hover state
|
||||
const getStatusColorClass = () => {
|
||||
const statusColors = {
|
||||
Standby: {
|
||||
default: "bg-vscode-descriptionForeground/40",
|
||||
hover: "bg-vscode-descriptionForeground/60",
|
||||
},
|
||||
Indexing: {
|
||||
default: "bg-yellow-500/40 animate-pulse",
|
||||
hover: "bg-yellow-500 animate-pulse",
|
||||
},
|
||||
Indexed: {
|
||||
default: "bg-green-500/40",
|
||||
hover: "bg-green-500",
|
||||
},
|
||||
Error: {
|
||||
default: "bg-red-500/40",
|
||||
hover: "bg-red-500",
|
||||
},
|
||||
}
|
||||
|
||||
const colors = statusColors[indexingStatus.systemStatus as keyof typeof statusColors] || statusColors.Standby
|
||||
return isHovered ? colors.hover : colors.default
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn("relative inline-block", className)}>
|
||||
<button
|
||||
onClick={handleClick}
|
||||
onMouseEnter={handleMouseEnterButton}
|
||||
onMouseLeave={handleMouseLeaveButton}
|
||||
className={cn(
|
||||
"flex items-center justify-center w-7 h-7 rounded-md",
|
||||
"bg-transparent hover:bg-vscode-list-hoverBackground",
|
||||
"cursor-pointer transition-all duration-200",
|
||||
"opacity-85 hover:opacity-100 relative",
|
||||
)}
|
||||
aria-label={getTooltipText()}>
|
||||
{/* Status dot */}
|
||||
<span
|
||||
className={cn(
|
||||
"inline-block w-2 h-2 rounded-full relative z-10 transition-colors duration-200",
|
||||
getStatusColorClass(),
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
{showTooltip && (
|
||||
<div
|
||||
className={cn(
|
||||
"absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2",
|
||||
"px-2 py-1 text-xs font-medium text-vscode-foreground",
|
||||
"bg-vscode-editor-background border border-vscode-panel-border",
|
||||
"rounded shadow-lg whitespace-nowrap z-50",
|
||||
)}
|
||||
role="tooltip">
|
||||
{getTooltipText()}
|
||||
<div
|
||||
className={cn(
|
||||
"absolute top-full left-1/2 transform -translate-x-1/2",
|
||||
"w-0 h-0 border-l-4 border-r-4 border-t-4",
|
||||
"border-l-transparent border-r-transparent border-t-vscode-panel-border",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -126,6 +126,9 @@ describe("ChatTextArea", () => {
|
|||
|
||||
render(<ChatTextArea {...defaultProps} inputValue="" />)
|
||||
|
||||
// Clear any calls from component initialization (e.g., IndexingStatusBadge)
|
||||
mockPostMessage.mockClear()
|
||||
|
||||
const enhanceButton = getEnhancePromptButton()
|
||||
fireEvent.click(enhanceButton)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,278 @@
|
|||
import React from "react"
|
||||
import { render, screen, fireEvent, waitFor, act } from "@testing-library/react"
|
||||
import { IndexingStatusDot } from "../IndexingStatusBadge"
|
||||
import { vscode } from "@src/utils/vscode"
|
||||
|
||||
// Mock i18n setup to prevent initialization errors
|
||||
jest.mock("@/i18n/setup", () => ({
|
||||
__esModule: true,
|
||||
default: {
|
||||
use: jest.fn().mockReturnThis(),
|
||||
init: jest.fn().mockReturnThis(),
|
||||
addResourceBundle: jest.fn(),
|
||||
language: "en",
|
||||
changeLanguage: jest.fn(),
|
||||
},
|
||||
loadTranslations: jest.fn(),
|
||||
}))
|
||||
|
||||
// Mock react-i18next
|
||||
jest.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string, params?: any) => {
|
||||
const translations: Record<string, string> = {
|
||||
"indexingStatus.ready": "Index ready",
|
||||
"indexingStatus.indexing": params?.progress !== undefined ? `Indexing ${params.progress}%` : "Indexing",
|
||||
"indexingStatus.error": "Index error",
|
||||
"indexingStatus.indexed": "Indexed",
|
||||
"indexingStatus.tooltip.ready": "The codebase index is ready for use",
|
||||
"indexingStatus.tooltip.indexing":
|
||||
params?.progress !== undefined
|
||||
? `Indexing in progress: ${params.progress}% complete`
|
||||
: "Indexing in progress",
|
||||
"indexingStatus.tooltip.error": "An error occurred during indexing",
|
||||
"indexingStatus.tooltip.indexed": "Codebase has been successfully indexed",
|
||||
"indexingStatus.tooltip.clickToSettings": "Click to open indexing settings",
|
||||
}
|
||||
return translations[key] || key
|
||||
},
|
||||
i18n: {
|
||||
language: "en",
|
||||
changeLanguage: jest.fn(),
|
||||
},
|
||||
}),
|
||||
initReactI18next: {
|
||||
type: "3rdParty",
|
||||
init: jest.fn(),
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock vscode API
|
||||
jest.mock("@src/utils/vscode", () => ({
|
||||
vscode: {
|
||||
postMessage: jest.fn(),
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock the useTooltip hook
|
||||
jest.mock("@/hooks/useTooltip", () => ({
|
||||
useTooltip: jest.fn(() => ({
|
||||
showTooltip: false,
|
||||
handleMouseEnter: jest.fn(),
|
||||
handleMouseLeave: jest.fn(),
|
||||
cleanup: jest.fn(),
|
||||
})),
|
||||
}))
|
||||
|
||||
// Mock the ExtensionStateContext
|
||||
jest.mock("@/context/ExtensionStateContext", () => ({
|
||||
useExtensionState: () => ({
|
||||
version: "1.0.0",
|
||||
clineMessages: [],
|
||||
taskHistory: [],
|
||||
shouldShowAnnouncement: false,
|
||||
language: "en",
|
||||
}),
|
||||
ExtensionStateContextProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
|
||||
}))
|
||||
|
||||
// Mock TranslationContext to provide t function directly
|
||||
jest.mock("@/i18n/TranslationContext", () => ({
|
||||
useAppTranslation: () => ({
|
||||
t: (key: string, params?: any) => {
|
||||
// Remove namespace prefix if present
|
||||
const cleanKey = key.includes(":") ? key.split(":")[1] : key
|
||||
|
||||
const translations: Record<string, string> = {
|
||||
"indexingStatus.ready": "Index ready",
|
||||
"indexingStatus.indexing":
|
||||
params?.percentage !== undefined ? `Indexing ${params.percentage}%` : "Indexing",
|
||||
"indexingStatus.error": "Index error",
|
||||
"indexingStatus.indexed": "Indexed",
|
||||
"indexingStatus.tooltip.ready": "The codebase index is ready for use",
|
||||
"indexingStatus.tooltip.indexing":
|
||||
params?.percentage !== undefined
|
||||
? `Indexing in progress: ${params.percentage}% complete`
|
||||
: "Indexing in progress",
|
||||
"indexingStatus.tooltip.error": "An error occurred during indexing",
|
||||
"indexingStatus.tooltip.indexed": "Codebase has been successfully indexed",
|
||||
"indexingStatus.tooltip.clickToSettings": "Click to open indexing settings",
|
||||
}
|
||||
return translations[cleanKey] || cleanKey
|
||||
},
|
||||
}),
|
||||
}))
|
||||
|
||||
describe("IndexingStatusDot", () => {
|
||||
const renderComponent = (props = {}) => {
|
||||
return render(<IndexingStatusDot {...props} />)
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("renders the status dot", () => {
|
||||
renderComponent()
|
||||
const button = screen.getByRole("button")
|
||||
expect(button).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("shows standby status by default", () => {
|
||||
renderComponent()
|
||||
const button = screen.getByRole("button")
|
||||
expect(button).toHaveAttribute("aria-label", "Index ready")
|
||||
})
|
||||
|
||||
it("posts settingsButtonClicked message when clicked", () => {
|
||||
// Mock window.postMessage
|
||||
const postMessageSpy = jest.spyOn(window, "postMessage")
|
||||
|
||||
renderComponent()
|
||||
|
||||
const button = screen.getByRole("button")
|
||||
fireEvent.click(button)
|
||||
|
||||
expect(postMessageSpy).toHaveBeenCalledWith(
|
||||
{
|
||||
type: "action",
|
||||
action: "settingsButtonClicked",
|
||||
values: { section: "experimental" },
|
||||
},
|
||||
"*",
|
||||
)
|
||||
|
||||
postMessageSpy.mockRestore()
|
||||
})
|
||||
|
||||
it("requests indexing status on mount", () => {
|
||||
renderComponent()
|
||||
|
||||
expect(vscode.postMessage).toHaveBeenCalledWith({
|
||||
type: "requestIndexingStatus",
|
||||
})
|
||||
})
|
||||
|
||||
it("updates status when receiving indexingStatusUpdate message", async () => {
|
||||
renderComponent()
|
||||
|
||||
// Simulate receiving an indexing status update
|
||||
const event = new MessageEvent("message", {
|
||||
data: {
|
||||
type: "indexingStatusUpdate",
|
||||
values: {
|
||||
systemStatus: "Indexing",
|
||||
processedItems: 50,
|
||||
totalItems: 100,
|
||||
currentItemUnit: "files",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
act(() => {
|
||||
window.dispatchEvent(event)
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
const button = screen.getByRole("button")
|
||||
expect(button).toHaveAttribute("aria-label", "Indexing 50%")
|
||||
})
|
||||
})
|
||||
|
||||
it("shows error status correctly", async () => {
|
||||
renderComponent()
|
||||
|
||||
// Simulate error status
|
||||
const event = new MessageEvent("message", {
|
||||
data: {
|
||||
type: "indexingStatusUpdate",
|
||||
values: {
|
||||
systemStatus: "Error",
|
||||
processedItems: 0,
|
||||
totalItems: 0,
|
||||
currentItemUnit: "files",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
act(() => {
|
||||
window.dispatchEvent(event)
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
const button = screen.getByRole("button")
|
||||
expect(button).toHaveAttribute("aria-label", "Index error")
|
||||
})
|
||||
})
|
||||
|
||||
it("shows indexed status correctly", async () => {
|
||||
renderComponent()
|
||||
|
||||
// Simulate indexed status
|
||||
const event = new MessageEvent("message", {
|
||||
data: {
|
||||
type: "indexingStatusUpdate",
|
||||
values: {
|
||||
systemStatus: "Indexed",
|
||||
processedItems: 100,
|
||||
totalItems: 100,
|
||||
currentItemUnit: "files",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
act(() => {
|
||||
window.dispatchEvent(event)
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
const button = screen.getByRole("button")
|
||||
expect(button).toHaveAttribute("aria-label", "Indexed")
|
||||
})
|
||||
})
|
||||
|
||||
it("cleans up event listener on unmount", () => {
|
||||
const { unmount } = renderComponent()
|
||||
const removeEventListenerSpy = jest.spyOn(window, "removeEventListener")
|
||||
|
||||
unmount()
|
||||
|
||||
expect(removeEventListenerSpy).toHaveBeenCalledWith("message", expect.any(Function))
|
||||
})
|
||||
|
||||
it("calculates progress percentage correctly", async () => {
|
||||
renderComponent()
|
||||
|
||||
// Test various progress scenarios
|
||||
const testCases = [
|
||||
{ processed: 0, total: 100, expected: 0 },
|
||||
{ processed: 25, total: 100, expected: 25 },
|
||||
{ processed: 33, total: 100, expected: 33 },
|
||||
{ processed: 100, total: 100, expected: 100 },
|
||||
{ processed: 0, total: 0, expected: 0 },
|
||||
]
|
||||
|
||||
for (const testCase of testCases) {
|
||||
const event = new MessageEvent("message", {
|
||||
data: {
|
||||
type: "indexingStatusUpdate",
|
||||
values: {
|
||||
systemStatus: "Indexing",
|
||||
processedItems: testCase.processed,
|
||||
totalItems: testCase.total,
|
||||
currentItemUnit: "files",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
act(() => {
|
||||
window.dispatchEvent(event)
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
const button = screen.getByRole("button")
|
||||
expect(button).toHaveAttribute("aria-label", `Indexing ${testCase.expected}%`)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
@ -40,16 +40,7 @@ interface CodeIndexSettingsProps {
|
|||
areSettingsCommitted: boolean
|
||||
}
|
||||
|
||||
interface IndexingStatusUpdateMessage {
|
||||
type: "indexingStatusUpdate"
|
||||
values: {
|
||||
systemStatus: string
|
||||
message?: string
|
||||
processedItems: number
|
||||
totalItems: number
|
||||
currentItemUnit?: string
|
||||
}
|
||||
}
|
||||
import type { IndexingStatusUpdateMessage } from "@roo/ExtensionMessage"
|
||||
|
||||
export const CodeIndexSettings: React.FC<CodeIndexSettingsProps> = ({
|
||||
codebaseIndexModels,
|
||||
|
|
|
|||
39
webview-ui/src/hooks/useTooltip.ts
Normal file
39
webview-ui/src/hooks/useTooltip.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import { useState, useCallback, useRef } from "react"
|
||||
|
||||
interface UseTooltipOptions {
|
||||
delay?: number
|
||||
}
|
||||
|
||||
export const useTooltip = (options: UseTooltipOptions = {}) => {
|
||||
const { delay = 300 } = options
|
||||
const [showTooltip, setShowTooltip] = useState(false)
|
||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null)
|
||||
|
||||
const handleMouseEnter = useCallback(() => {
|
||||
if (timeoutRef.current) clearTimeout(timeoutRef.current)
|
||||
timeoutRef.current = setTimeout(() => setShowTooltip(true), delay)
|
||||
}, [delay])
|
||||
|
||||
const handleMouseLeave = useCallback(() => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current)
|
||||
timeoutRef.current = null
|
||||
}
|
||||
setShowTooltip(false)
|
||||
}, [])
|
||||
|
||||
// Cleanup on unmount
|
||||
const cleanup = useCallback(() => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current)
|
||||
timeoutRef.current = null
|
||||
}
|
||||
}, [])
|
||||
|
||||
return {
|
||||
showTooltip,
|
||||
handleMouseEnter,
|
||||
handleMouseLeave,
|
||||
cleanup,
|
||||
}
|
||||
}
|
||||
|
|
@ -279,5 +279,12 @@
|
|||
"deny": {
|
||||
"title": "Denegar tot"
|
||||
}
|
||||
},
|
||||
"indexingStatus": {
|
||||
"ready": "Índex preparat",
|
||||
"indexing": "Indexant {{percentage}}%",
|
||||
"indexed": "Indexat",
|
||||
"error": "Error d'índex",
|
||||
"status": "Estat de l'índex"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,5 +279,12 @@
|
|||
"deny": {
|
||||
"title": "Alle ablehnen"
|
||||
}
|
||||
},
|
||||
"indexingStatus": {
|
||||
"ready": "Index bereit",
|
||||
"indexing": "Indizierung {{percentage}}%",
|
||||
"indexed": "Indiziert",
|
||||
"error": "Index-Fehler",
|
||||
"status": "Index-Status"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,5 +279,12 @@
|
|||
"description": "Roo has reached the auto-approved limit of {{count}} API request(s). Would you like to reset the count and proceed with the task?",
|
||||
"button": "Reset and Continue"
|
||||
}
|
||||
},
|
||||
"indexingStatus": {
|
||||
"ready": "Index ready",
|
||||
"indexing": "Indexing {{percentage}}%",
|
||||
"indexed": "Indexed",
|
||||
"error": "Index error",
|
||||
"status": "Index status"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,5 +279,12 @@
|
|||
"deny": {
|
||||
"title": "Denegar todo"
|
||||
}
|
||||
},
|
||||
"indexingStatus": {
|
||||
"ready": "Índice listo",
|
||||
"indexing": "Indexando {{percentage}}%",
|
||||
"indexed": "Indexado",
|
||||
"error": "Error de índice",
|
||||
"status": "Estado del índice"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,5 +279,12 @@
|
|||
"deny": {
|
||||
"title": "Tout refuser"
|
||||
}
|
||||
},
|
||||
"indexingStatus": {
|
||||
"ready": "Index prêt",
|
||||
"indexing": "Indexation {{percentage}}%",
|
||||
"indexed": "Indexé",
|
||||
"error": "Erreur d'index",
|
||||
"status": "Statut de l'index"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,5 +279,12 @@
|
|||
"deny": {
|
||||
"title": "सभी अस्वीकार करें"
|
||||
}
|
||||
},
|
||||
"indexingStatus": {
|
||||
"ready": "इंडेक्स तैयार",
|
||||
"indexing": "इंडेक्सिंग {{percentage}}%",
|
||||
"indexed": "इंडेक्स किया गया",
|
||||
"error": "इंडेक्स त्रुटि",
|
||||
"status": "इंडेक्स स्थिति"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,5 +279,12 @@
|
|||
"deny": {
|
||||
"title": "Nega tutto"
|
||||
}
|
||||
},
|
||||
"indexingStatus": {
|
||||
"ready": "Indice pronto",
|
||||
"indexing": "Indicizzazione {{percentage}}%",
|
||||
"indexed": "Indicizzato",
|
||||
"error": "Errore indice",
|
||||
"status": "Stato indice"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,5 +279,12 @@
|
|||
"deny": {
|
||||
"title": "すべて拒否"
|
||||
}
|
||||
},
|
||||
"indexingStatus": {
|
||||
"ready": "インデックス準備完了",
|
||||
"indexing": "インデックス作成中 {{percentage}}%",
|
||||
"indexed": "インデックス作成済み",
|
||||
"error": "インデックスエラー",
|
||||
"status": "インデックス状態"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,5 +279,12 @@
|
|||
"deny": {
|
||||
"title": "모두 거부"
|
||||
}
|
||||
},
|
||||
"indexingStatus": {
|
||||
"ready": "인덱스 준비됨",
|
||||
"indexing": "인덱싱 중 {{percentage}}%",
|
||||
"indexed": "인덱싱 완료",
|
||||
"error": "인덱스 오류",
|
||||
"status": "인덱스 상태"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,5 +279,12 @@
|
|||
"deny": {
|
||||
"title": "Alles weigeren"
|
||||
}
|
||||
},
|
||||
"indexingStatus": {
|
||||
"ready": "Index gereed",
|
||||
"indexing": "Indexeren {{percentage}}%",
|
||||
"indexed": "Geïndexeerd",
|
||||
"error": "Index fout",
|
||||
"status": "Index status"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,5 +279,12 @@
|
|||
"deny": {
|
||||
"title": "Odrzuć wszystko"
|
||||
}
|
||||
},
|
||||
"indexingStatus": {
|
||||
"ready": "Indeks gotowy",
|
||||
"indexing": "Indeksowanie {{percentage}}%",
|
||||
"indexed": "Zaindeksowane",
|
||||
"error": "Błąd indeksu",
|
||||
"status": "Status indeksu"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,5 +279,12 @@
|
|||
"deny": {
|
||||
"title": "Negar tudo"
|
||||
}
|
||||
},
|
||||
"indexingStatus": {
|
||||
"ready": "Índice pronto",
|
||||
"indexing": "Indexando {{percentage}}%",
|
||||
"indexed": "Indexado",
|
||||
"error": "Erro do índice",
|
||||
"status": "Status do índice"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,5 +279,12 @@
|
|||
"deny": {
|
||||
"title": "Отклонить все"
|
||||
}
|
||||
},
|
||||
"indexingStatus": {
|
||||
"ready": "Индекс готов",
|
||||
"indexing": "Индексация {{percentage}}%",
|
||||
"indexed": "Проиндексировано",
|
||||
"error": "Ошибка индекса",
|
||||
"status": "Статус индекса"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,5 +279,12 @@
|
|||
"deny": {
|
||||
"title": "Tümünü Reddet"
|
||||
}
|
||||
},
|
||||
"indexingStatus": {
|
||||
"ready": "İndeks hazır",
|
||||
"indexing": "İndeksleniyor {{percentage}}%",
|
||||
"indexed": "İndekslendi",
|
||||
"error": "İndeks hatası",
|
||||
"status": "İndeks durumu"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,5 +279,12 @@
|
|||
"deny": {
|
||||
"title": "Từ chối tất cả"
|
||||
}
|
||||
},
|
||||
"indexingStatus": {
|
||||
"ready": "Chỉ mục sẵn sàng",
|
||||
"indexing": "Đang lập chỉ mục {{percentage}}%",
|
||||
"indexed": "Đã lập chỉ mục",
|
||||
"error": "Lỗi chỉ mục",
|
||||
"status": "Trạng thái chỉ mục"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,5 +279,12 @@
|
|||
"deny": {
|
||||
"title": "全部拒绝"
|
||||
}
|
||||
},
|
||||
"indexingStatus": {
|
||||
"ready": "索引就绪",
|
||||
"indexing": "索引中 {{percentage}}%",
|
||||
"indexed": "已索引",
|
||||
"error": "索引错误",
|
||||
"status": "索引状态"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,5 +279,12 @@
|
|||
"deny": {
|
||||
"title": "全部拒絕"
|
||||
}
|
||||
},
|
||||
"indexingStatus": {
|
||||
"ready": "索引就緒",
|
||||
"indexing": "索引中 {{percentage}}%",
|
||||
"indexed": "已索引",
|
||||
"error": "索引錯誤",
|
||||
"status": "索引狀態"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue