@open-mercato/search 0.6.7-develop.6860.1.f796b22e80 → 0.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +1 -7
- package/dist/lib/presenter-enricher.js +12 -12
- package/dist/lib/presenter-enricher.js.map +2 -2
- package/dist/modules/search/ai-tools.js +2 -5
- package/dist/modules/search/ai-tools.js.map +2 -2
- package/dist/modules/search/api/openapi.js +0 -2
- package/dist/modules/search/api/openapi.js.map +2 -2
- package/dist/modules/search/api/settings/vector-store/route.js +0 -34
- package/dist/modules/search/api/settings/vector-store/route.js.map +2 -2
- package/dist/modules/search/frontend/components/GlobalSearchDialog.js +9 -2
- package/dist/modules/search/frontend/components/GlobalSearchDialog.js.map +2 -2
- package/dist/modules/search/frontend/components/HybridSearchTable.js +11 -2
- package/dist/modules/search/frontend/components/HybridSearchTable.js.map +2 -2
- package/dist/modules/search/frontend/components/SearchSettingsPageClient.js.map +2 -2
- package/dist/modules/search/frontend/components/TopbarSearchInline.js +9 -2
- package/dist/modules/search/frontend/components/TopbarSearchInline.js.map +2 -2
- package/dist/modules/search/frontend/components/sections/VectorSearchSection.js +3 -15
- package/dist/modules/search/frontend/components/sections/VectorSearchSection.js.map +2 -2
- package/dist/modules/search/i18n/de.json +0 -49
- package/dist/modules/search/i18n/en.json +0 -49
- package/dist/modules/search/i18n/es.json +0 -49
- package/dist/modules/search/i18n/pl.json +0 -49
- package/dist/modules/search/lib/auto-indexing.js +8 -7
- package/dist/modules/search/lib/auto-indexing.js.map +2 -2
- package/dist/service.js +5 -2
- package/dist/service.js.map +2 -2
- package/dist/strategies/vector.strategy.js +1 -3
- package/dist/strategies/vector.strategy.js.map +2 -2
- package/dist/vector/drivers/pgvector/index.js +1 -55
- package/dist/vector/drivers/pgvector/index.js.map +2 -2
- package/dist/vector/types.js.map +2 -2
- package/package.json +10 -11
- package/src/__tests__/presenter-enricher.test.ts +0 -48
- package/src/lib/presenter-enricher.ts +14 -14
- package/src/modules/search/README.md +1 -4
- package/src/modules/search/ai-tools.ts +2 -5
- package/src/modules/search/api/openapi.ts +0 -2
- package/src/modules/search/api/settings/vector-store/route.ts +0 -43
- package/src/modules/search/frontend/components/GlobalSearchDialog.tsx +15 -2
- package/src/modules/search/frontend/components/HybridSearchTable.tsx +17 -2
- package/src/modules/search/frontend/components/SearchSettingsPageClient.tsx +0 -2
- package/src/modules/search/frontend/components/TopbarSearchInline.tsx +15 -2
- package/src/modules/search/frontend/components/sections/VectorSearchSection.tsx +2 -32
- package/src/modules/search/frontend/components/sections/__tests__/VectorSearchSection.test.tsx +1 -36
- package/src/modules/search/i18n/de.json +0 -49
- package/src/modules/search/i18n/en.json +0 -49
- package/src/modules/search/i18n/es.json +0 -49
- package/src/modules/search/i18n/pl.json +0 -49
- package/src/modules/search/lib/auto-indexing.ts +12 -7
- package/src/service.ts +7 -2
- package/src/strategies/vector.strategy.ts +1 -7
- package/src/vector/drivers/pgvector/index.ts +1 -77
- package/src/vector/types.ts +0 -14
- package/dist/modules/search/events.js +0 -13
- package/dist/modules/search/events.js.map +0 -7
- package/dist/modules/search/frontend/lib/entityTypeLabel.js +0 -19
- package/dist/modules/search/frontend/lib/entityTypeLabel.js.map +0 -7
- package/dist/modules/search/i18n/ko.json +0 -239
- package/src/__tests__/pgvector-extension-availability.test.ts +0 -158
- package/src/__tests__/presenter-locale.test.ts +0 -76
- package/src/modules/search/api/__tests__/global-search.routes.test.ts +0 -167
- package/src/modules/search/events.ts +0 -10
- package/src/modules/search/frontend/lib/__tests__/entityTypeLabel.test.ts +0 -25
- package/src/modules/search/frontend/lib/entityTypeLabel.ts +0 -22
- package/src/modules/search/i18n/ko.json +0 -239
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
jest.mock('next/headers', () => ({
|
|
2
|
-
cookies: async () => ({ get: (name: string) => (name === 'locale' ? { value: 'pl' } : undefined) }),
|
|
3
|
-
headers: async () => ({ get: () => '' }),
|
|
4
|
-
}))
|
|
5
|
-
|
|
6
|
-
import type { Kysely } from 'kysely'
|
|
7
|
-
import { createPresenterEnricher } from '../lib/presenter-enricher'
|
|
8
|
-
import { registerModules, resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
9
|
-
import type { EntityId } from '@open-mercato/shared/modules/entities'
|
|
10
|
-
import type { Module } from '@open-mercato/shared/modules/registry'
|
|
11
|
-
import type { SearchEntityConfig, SearchResult } from '../types'
|
|
12
|
-
|
|
13
|
-
const DEMO_ENTITY_ID = 'demo:thing' as EntityId
|
|
14
|
-
|
|
15
|
-
function makeDbReturning(rows: Array<{ entity_type: string; entity_id: string; doc: Record<string, unknown> }>) {
|
|
16
|
-
const chain = {
|
|
17
|
-
selectFrom: () => chain,
|
|
18
|
-
select: () => chain,
|
|
19
|
-
where: () => chain,
|
|
20
|
-
execute: async () => rows,
|
|
21
|
-
}
|
|
22
|
-
return chain as unknown as Kysely<Record<string, never>>
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
describe('request-time presenter localization', () => {
|
|
26
|
-
beforeAll(() => {
|
|
27
|
-
registerModules([
|
|
28
|
-
{
|
|
29
|
-
id: 'demo',
|
|
30
|
-
translations: {
|
|
31
|
-
en: { 'demo.search.badge': 'Person', 'demo.search.link.open': 'Open person' },
|
|
32
|
-
pl: { 'demo.search.badge': 'Osoba', 'demo.search.link.open': 'Otwórz osobę' },
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
] satisfies Module[])
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
it('renders the presenter badge and link label in the request locale (pl)', async () => {
|
|
39
|
-
const { locale, t } = await resolveTranslations()
|
|
40
|
-
expect(locale).toBe('pl')
|
|
41
|
-
expect(t('demo.search.badge', 'Person')).toBe('Osoba')
|
|
42
|
-
|
|
43
|
-
const config: SearchEntityConfig = {
|
|
44
|
-
entityId: DEMO_ENTITY_ID,
|
|
45
|
-
enabled: true,
|
|
46
|
-
formatResult: async () => {
|
|
47
|
-
const { t: translate } = await resolveTranslations()
|
|
48
|
-
return { title: 'Ada', badge: translate('demo.search.badge', 'Person') }
|
|
49
|
-
},
|
|
50
|
-
resolveLinks: async () => {
|
|
51
|
-
const { t: translate } = await resolveTranslations()
|
|
52
|
-
return [{ href: '/x', label: translate('demo.search.link.open', 'Open person') }]
|
|
53
|
-
},
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const entityConfigMap = new Map<EntityId, SearchEntityConfig>([[DEMO_ENTITY_ID, config]])
|
|
57
|
-
const db = makeDbReturning([
|
|
58
|
-
{ entity_type: 'demo:thing', entity_id: 'rec-1', doc: { id: 'rec-1', display_name: 'Ada' } },
|
|
59
|
-
])
|
|
60
|
-
|
|
61
|
-
const enrich = createPresenterEnricher(db, entityConfigMap)
|
|
62
|
-
const results: SearchResult[] = [{
|
|
63
|
-
entityId: DEMO_ENTITY_ID,
|
|
64
|
-
recordId: 'rec-1',
|
|
65
|
-
score: 1,
|
|
66
|
-
source: 'fulltext',
|
|
67
|
-
presenter: { title: 'Ada', badge: 'Person' },
|
|
68
|
-
links: [{ href: '/x', label: 'Open person', kind: 'primary' }],
|
|
69
|
-
}]
|
|
70
|
-
|
|
71
|
-
const [enriched] = await enrich(results, 'tenant-1', null)
|
|
72
|
-
|
|
73
|
-
expect(enriched.presenter?.badge).toBe('Osoba')
|
|
74
|
-
expect(enriched.links?.[0]?.label).toBe('Otwórz osobę')
|
|
75
|
-
})
|
|
76
|
-
})
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
const mockGetAuthFromRequest = jest.fn()
|
|
2
|
-
jest.mock('@open-mercato/shared/lib/auth/server', () => ({
|
|
3
|
-
getAuthFromRequest: (...args: unknown[]) => mockGetAuthFromRequest(...args),
|
|
4
|
-
}))
|
|
5
|
-
|
|
6
|
-
const mockCreateRequestContainer = jest.fn()
|
|
7
|
-
jest.mock('@open-mercato/shared/lib/di/container', () => ({
|
|
8
|
-
createRequestContainer: (...args: unknown[]) => mockCreateRequestContainer(...args),
|
|
9
|
-
}))
|
|
10
|
-
|
|
11
|
-
const mockResolveOrganizationScopeForRequest = jest.fn()
|
|
12
|
-
jest.mock('@open-mercato/core/modules/directory/utils/organizationScope', () => ({
|
|
13
|
-
resolveOrganizationScopeForRequest: (...args: unknown[]) => mockResolveOrganizationScopeForRequest(...args),
|
|
14
|
-
}))
|
|
15
|
-
|
|
16
|
-
jest.mock('../../lib/embedding-config', () => ({
|
|
17
|
-
resolveEmbeddingConfig: jest.fn().mockResolvedValue(null),
|
|
18
|
-
}))
|
|
19
|
-
|
|
20
|
-
jest.mock('../../lib/global-search-config', () => ({
|
|
21
|
-
resolveGlobalSearchStrategies: jest.fn().mockResolvedValue(['fulltext', 'vector', 'tokens']),
|
|
22
|
-
}))
|
|
23
|
-
|
|
24
|
-
jest.mock('next/headers', () => ({
|
|
25
|
-
cookies: async () => ({ get: () => undefined }),
|
|
26
|
-
headers: async () => ({ get: (name: string) => (name === 'accept-language' ? 'pl-PL' : null) }),
|
|
27
|
-
}))
|
|
28
|
-
|
|
29
|
-
import type { Kysely } from 'kysely'
|
|
30
|
-
import type { EntityId } from '@open-mercato/shared/modules/entities'
|
|
31
|
-
import type { Module } from '@open-mercato/shared/modules/registry'
|
|
32
|
-
import type { SearchEntityConfig, SearchResult, SearchStrategy, SearchStrategyId } from '../../../../types'
|
|
33
|
-
import { registerModules, resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
34
|
-
import { SearchService } from '../../../../service'
|
|
35
|
-
import { createPresenterEnricher } from '../../../../lib/presenter-enricher'
|
|
36
|
-
import { GET } from '../search/global/route'
|
|
37
|
-
|
|
38
|
-
const DEMO_ENTITY_ID = 'demo:thing' as EntityId
|
|
39
|
-
|
|
40
|
-
function createDatabase(rows: Array<{ entity_type: string; entity_id: string; doc: Record<string, unknown> }>) {
|
|
41
|
-
const chain = {
|
|
42
|
-
selectFrom: () => chain,
|
|
43
|
-
select: () => chain,
|
|
44
|
-
where: () => chain,
|
|
45
|
-
execute: async () => rows,
|
|
46
|
-
}
|
|
47
|
-
return chain as unknown as Kysely<Record<string, never>>
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function createStrategy(source: SearchStrategyId, recordId: string): SearchStrategy {
|
|
51
|
-
const result: SearchResult = {
|
|
52
|
-
entityId: DEMO_ENTITY_ID,
|
|
53
|
-
recordId,
|
|
54
|
-
organizationId: 'org-1',
|
|
55
|
-
score: 1,
|
|
56
|
-
source,
|
|
57
|
-
presenter: { title: recordId, badge: 'Person' },
|
|
58
|
-
links: [{ href: `/backend/demo/${recordId}`, label: 'Open person', kind: 'primary' }],
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
id: source,
|
|
63
|
-
name: source,
|
|
64
|
-
priority: 10,
|
|
65
|
-
isAvailable: async () => true,
|
|
66
|
-
ensureReady: async () => undefined,
|
|
67
|
-
search: async () => [result],
|
|
68
|
-
index: async () => undefined,
|
|
69
|
-
delete: async () => undefined,
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
describe('GET /api/search/search/global presenter localization', () => {
|
|
74
|
-
beforeAll(() => {
|
|
75
|
-
registerModules([
|
|
76
|
-
{
|
|
77
|
-
id: 'demo',
|
|
78
|
-
translations: {
|
|
79
|
-
en: {
|
|
80
|
-
'demo.search.badge': 'Person',
|
|
81
|
-
'demo.search.link.open': 'Open person',
|
|
82
|
-
},
|
|
83
|
-
pl: {
|
|
84
|
-
'demo.search.badge': 'Osoba',
|
|
85
|
-
'demo.search.link.open': 'Otwórz osobę',
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
] satisfies Module[])
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
beforeEach(() => {
|
|
93
|
-
jest.clearAllMocks()
|
|
94
|
-
mockGetAuthFromRequest.mockResolvedValue({
|
|
95
|
-
tenantId: 'tenant-1',
|
|
96
|
-
orgId: 'org-1',
|
|
97
|
-
sub: 'user-1',
|
|
98
|
-
isSuperAdmin: false,
|
|
99
|
-
})
|
|
100
|
-
mockResolveOrganizationScopeForRequest.mockResolvedValue({
|
|
101
|
-
selectedId: 'org-1',
|
|
102
|
-
filterIds: ['org-1'],
|
|
103
|
-
allowedIds: ['org-1'],
|
|
104
|
-
tenantId: 'tenant-1',
|
|
105
|
-
})
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
it('replaces frozen presenters and links for fulltext, vector, and tokens using Accept-Language', async () => {
|
|
109
|
-
const rows = ['fulltext-record', 'vector-record', 'tokens-record'].map((recordId) => ({
|
|
110
|
-
entity_type: DEMO_ENTITY_ID,
|
|
111
|
-
entity_id: recordId,
|
|
112
|
-
doc: { id: recordId, title: recordId },
|
|
113
|
-
}))
|
|
114
|
-
const config: SearchEntityConfig = {
|
|
115
|
-
entityId: DEMO_ENTITY_ID,
|
|
116
|
-
enabled: true,
|
|
117
|
-
formatResult: async (context) => {
|
|
118
|
-
const { t } = await resolveTranslations()
|
|
119
|
-
return {
|
|
120
|
-
title: String(context.record.title),
|
|
121
|
-
badge: t('demo.search.badge', 'Person'),
|
|
122
|
-
}
|
|
123
|
-
},
|
|
124
|
-
resolveLinks: async (context) => {
|
|
125
|
-
const { t } = await resolveTranslations()
|
|
126
|
-
return [{
|
|
127
|
-
href: `/backend/demo/${String(context.record.id)}`,
|
|
128
|
-
label: t('demo.search.link.open', 'Open person'),
|
|
129
|
-
kind: 'primary',
|
|
130
|
-
}]
|
|
131
|
-
},
|
|
132
|
-
}
|
|
133
|
-
const configMap = new Map<EntityId, SearchEntityConfig>([[DEMO_ENTITY_ID, config]])
|
|
134
|
-
const presenterEnricher = createPresenterEnricher(createDatabase(rows), configMap)
|
|
135
|
-
const searchService = new SearchService({
|
|
136
|
-
strategies: [
|
|
137
|
-
createStrategy('fulltext', 'fulltext-record'),
|
|
138
|
-
createStrategy('vector', 'vector-record'),
|
|
139
|
-
createStrategy('tokens', 'tokens-record'),
|
|
140
|
-
],
|
|
141
|
-
defaultStrategies: ['fulltext', 'vector', 'tokens'],
|
|
142
|
-
presenterEnricher,
|
|
143
|
-
})
|
|
144
|
-
const container = {
|
|
145
|
-
resolve: jest.fn((name: string) => (name === 'searchService' ? searchService : undefined)),
|
|
146
|
-
dispose: jest.fn().mockResolvedValue(undefined),
|
|
147
|
-
}
|
|
148
|
-
mockCreateRequestContainer.mockResolvedValue(container)
|
|
149
|
-
|
|
150
|
-
const request = new Request('http://localhost/api/search/search/global?q=person', {
|
|
151
|
-
headers: { 'accept-language': 'pl-PL' },
|
|
152
|
-
})
|
|
153
|
-
const response = await GET(request)
|
|
154
|
-
const body = await response.json() as {
|
|
155
|
-
results: SearchResult[]
|
|
156
|
-
strategiesUsed: SearchStrategyId[]
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
expect(response.status).toBe(200)
|
|
160
|
-
expect(body.results).toHaveLength(3)
|
|
161
|
-
expect(body.strategiesUsed).toEqual(expect.arrayContaining(['fulltext', 'vector', 'tokens']))
|
|
162
|
-
for (const result of body.results) {
|
|
163
|
-
expect(result.presenter?.badge).toBe('Osoba')
|
|
164
|
-
expect(result.links?.[0]?.label).toBe('Otwórz osobę')
|
|
165
|
-
}
|
|
166
|
-
})
|
|
167
|
-
})
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { createModuleEvents } from '@open-mercato/shared/modules/events'
|
|
2
|
-
|
|
3
|
-
const events = [
|
|
4
|
-
{ id: 'search.index_record', label: 'Index Record', category: 'lifecycle' },
|
|
5
|
-
] as const
|
|
6
|
-
|
|
7
|
-
export const eventsConfig = createModuleEvents({ moduleId: 'search', events })
|
|
8
|
-
export const emitSearchEvent = eventsConfig.emit
|
|
9
|
-
export type SearchEventId = typeof events[number]['id']
|
|
10
|
-
export default eventsConfig
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { formatEntityId, resolveEntityTypeLabel } from '../entityTypeLabel'
|
|
2
|
-
import type { TranslateFn } from '@open-mercato/shared/lib/i18n/context'
|
|
3
|
-
|
|
4
|
-
describe('formatEntityId', () => {
|
|
5
|
-
it('humanizes module · entity', () => {
|
|
6
|
-
expect(formatEntityId('customers:customer_person_profile')).toBe('Customers · Customer Person Profile')
|
|
7
|
-
})
|
|
8
|
-
it('humanizes a bare segment', () => {
|
|
9
|
-
expect(formatEntityId('messages')).toBe('Messages')
|
|
10
|
-
})
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
describe('resolveEntityTypeLabel', () => {
|
|
14
|
-
const t: TranslateFn = (key, fallbackOrParams) =>
|
|
15
|
-
key === 'search.entityType.sales.sales_order'
|
|
16
|
-
? 'Order'
|
|
17
|
-
: (typeof fallbackOrParams === 'string' ? fallbackOrParams : key)
|
|
18
|
-
|
|
19
|
-
it('returns the translated label when a key exists', () => {
|
|
20
|
-
expect(resolveEntityTypeLabel(t, 'sales:sales_order')).toBe('Order')
|
|
21
|
-
})
|
|
22
|
-
it('falls back to the humanized string for unknown entity types', () => {
|
|
23
|
-
expect(resolveEntityTypeLabel(t, 'thirdparty:widget_thing')).toBe('Thirdparty · Widget Thing')
|
|
24
|
-
})
|
|
25
|
-
})
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { TranslateFn } from '@open-mercato/shared/lib/i18n/context'
|
|
2
|
-
|
|
3
|
-
function humanizeSegment(segment: string): string {
|
|
4
|
-
return segment
|
|
5
|
-
.split(/[_-]+/)
|
|
6
|
-
.filter(Boolean)
|
|
7
|
-
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
8
|
-
.join(' ')
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function formatEntityId(entityId: string): string {
|
|
12
|
-
if (!entityId.includes(':')) return humanizeSegment(entityId)
|
|
13
|
-
const [module, entity] = entityId.split(':')
|
|
14
|
-
return `${humanizeSegment(module)} · ${humanizeSegment(entity)}`
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function resolveEntityTypeLabel(t: TranslateFn, entityId: string): string {
|
|
18
|
-
const fallback = formatEntityId(entityId)
|
|
19
|
-
if (!entityId.includes(':')) return fallback
|
|
20
|
-
const [module, entity] = entityId.split(':')
|
|
21
|
-
return t(`search.entityType.${module}.${entity}`, fallback)
|
|
22
|
-
}
|
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"search.api.errors.autoIndexingDisabled": "OM_DISABLE_VECTOR_SEARCH_AUTOINDEXING(레거시 별칭: DISABLE_VECTOR_SEARCH_AUTOINDEXING)에 의해 자동 인덱싱이 비활성화되어 있습니다.",
|
|
3
|
-
"search.api.errors.configUnavailable": "구성 서비스를 사용할 수 없습니다.",
|
|
4
|
-
"search.api.errors.confirmAllRequired": "모든 항목에 대한 확인이 필요합니다.",
|
|
5
|
-
"search.api.errors.indexFetchFailed": "인덱스 데이터를 가져오지 못했습니다.",
|
|
6
|
-
"search.api.errors.indexUnavailable": "검색 인덱스를 사용할 수 없습니다.",
|
|
7
|
-
"search.api.errors.indexerUnavailable": "검색 인덱서를 사용할 수 없습니다.",
|
|
8
|
-
"search.api.errors.invalidOllamaBaseUrl": "Ollama 기본 URL이 허용되지 않습니다. 환경 변수에 OLLAMA_BASE_URL을 설정하거나 OM_SEARCH_OLLAMA_BASE_URL_ALLOWLIST에 호스트를 추가하세요.",
|
|
9
|
-
"search.api.errors.invalidProvider": "잘못된 임베딩 제공업체입니다.",
|
|
10
|
-
"search.api.errors.invalidStrategies": "지정된 검색 전략이 잘못되었습니다.",
|
|
11
|
-
"search.api.errors.lockFailed": "인덱스 잠금을 획득하지 못했습니다.",
|
|
12
|
-
"search.api.errors.missingQuery": "검색어는 필수입니다.",
|
|
13
|
-
"search.api.errors.noIndexableStrategy": "사용 가능한 인덱싱 전략이 없습니다.",
|
|
14
|
-
"search.api.errors.providerNotConfigured": "제공업체가 구성되지 않았습니다. 필요한 환경 변수를 설정하세요.",
|
|
15
|
-
"search.api.errors.providerUnavailable": "선택한 제공자를 사용할 수 없습니다.",
|
|
16
|
-
"search.api.errors.purgeFailed": "검색 인덱스 삭제에 실패했습니다.",
|
|
17
|
-
"search.api.errors.recreateFailed": "새 차원으로 벡터 테이블을 재생성하지 못했습니다.",
|
|
18
|
-
"search.api.errors.reindexFailed": "재인덱싱 작업에 실패했습니다.",
|
|
19
|
-
"search.api.errors.reindexInProgress": "이미 재인덱싱이 진행 중입니다.",
|
|
20
|
-
"search.api.errors.scopedQueueCancelFailed": "대기 중인 재인덱싱 작업 취소에 실패했습니다.",
|
|
21
|
-
"search.api.errors.scopedQueueCancelUnavailable": "범위 지정 대기열 취소를 사용할 수 없습니다.",
|
|
22
|
-
"search.api.errors.searchFailed": "검색 작업에 실패했습니다.",
|
|
23
|
-
"search.api.errors.serviceUnavailable": "검색 서비스를 사용할 수 없습니다.",
|
|
24
|
-
"search.api.errors.strategyUnavailable": "요청한 검색 전략을 사용할 수 없습니다.",
|
|
25
|
-
"search.api.errors.updateFailed": "검색 설정 업데이트에 실패했습니다.",
|
|
26
|
-
"search.api.errors.vectorUnavailable": "벡터 검색을 사용할 수 없습니다.",
|
|
27
|
-
"search.config.nav.hybridSearch": "검색 설정",
|
|
28
|
-
"search.dialog.actions.cancel": "취소 (Esc)",
|
|
29
|
-
"search.dialog.actions.clear": "검색 지우기",
|
|
30
|
-
"search.dialog.actions.openGlobalSearch": "전역 검색 열기",
|
|
31
|
-
"search.dialog.actions.openSelected": "열기 (Enter)",
|
|
32
|
-
"search.dialog.actions.search": "검색",
|
|
33
|
-
"search.dialog.empty.hint": "인덱싱된 레코드를 검색하려면 최소 {{count}}자를 입력하세요.",
|
|
34
|
-
"search.dialog.empty.none": "결과를 찾을 수 없습니다.",
|
|
35
|
-
"search.dialog.errors.noPermission": "전역 검색을 사용할 권한이 없습니다.",
|
|
36
|
-
"search.dialog.errors.searchFailed": "검색에 실패했습니다.",
|
|
37
|
-
"search.dialog.input.placeholder": "고객, 메모, 딜, 할 일 검색...",
|
|
38
|
-
"search.dialog.instructions": "인덱싱된 레코드를 검색하려면 입력하세요. 화살표 키로 결과를 탐색하세요.",
|
|
39
|
-
"search.dialog.noLink": "링크 없음",
|
|
40
|
-
"search.dialog.noLinkHint": "이 엔터티 유형에 대한 search.ts 구성이 없습니다",
|
|
41
|
-
"search.dialog.score.title": "관련성 점수",
|
|
42
|
-
"search.dialog.shortcuts.hint": "Cmd+K를 눌러 전환하세요. Enter로 엽니다",
|
|
43
|
-
"search.dialog.strategies.exact": "정확히 일치",
|
|
44
|
-
"search.dialog.strategies.exactDescription": "정확한 키워드 매칭",
|
|
45
|
-
"search.dialog.strategies.fuzzy": "유사 검색",
|
|
46
|
-
"search.dialog.strategies.fuzzyDescription": "오타에 강한 전체 텍스트 검색",
|
|
47
|
-
"search.dialog.strategies.label": "검색 방식:",
|
|
48
|
-
"search.dialog.strategies.semantic": "의미 기반",
|
|
49
|
-
"search.dialog.strategies.semanticDescription": "AI 기반 의미 기반 검색",
|
|
50
|
-
"search.dialog.strategies.toggle": "검색 옵션",
|
|
51
|
-
"search.dialog.title": "전역 검색",
|
|
52
|
-
"search.dialog.warnings.configureLink": "검색 설정 구성",
|
|
53
|
-
"search.entityType.catalog.catalog_offer": "권하다",
|
|
54
|
-
"search.entityType.catalog.catalog_option_schema_template": "옵션 템플릿",
|
|
55
|
-
"search.entityType.catalog.catalog_price_kind": "가격 유형",
|
|
56
|
-
"search.entityType.catalog.catalog_product": "제품",
|
|
57
|
-
"search.entityType.catalog.catalog_product_category": "범주",
|
|
58
|
-
"search.entityType.catalog.catalog_product_tag": "꼬리표",
|
|
59
|
-
"search.entityType.catalog.catalog_product_unit_conversion": "단위 변환",
|
|
60
|
-
"search.entityType.catalog.catalog_product_variant": "변종",
|
|
61
|
-
"search.entityType.customer_accounts.customer_role": "고객 역할",
|
|
62
|
-
"search.entityType.customer_accounts.customer_user": "고객 사용자",
|
|
63
|
-
"search.entityType.customers.customer_activity": "활동",
|
|
64
|
-
"search.entityType.customers.customer_comment": "논평",
|
|
65
|
-
"search.entityType.customers.customer_company_profile": "회사",
|
|
66
|
-
"search.entityType.customers.customer_deal": "거래",
|
|
67
|
-
"search.entityType.customers.customer_entity": "고객",
|
|
68
|
-
"search.entityType.customers.customer_person_profile": "사람",
|
|
69
|
-
"search.entityType.customers.customer_todo_link": "일",
|
|
70
|
-
"search.entityType.inbox_ops.inbox_proposal": "받은 편지함 제안",
|
|
71
|
-
"search.entityType.messages.message": "메시지",
|
|
72
|
-
"search.entityType.planner.planner_availability_rule_set": "가용성 규칙",
|
|
73
|
-
"search.entityType.resources.resources_resource": "의지",
|
|
74
|
-
"search.entityType.resources.resources_resource_type": "리소스 유형",
|
|
75
|
-
"search.entityType.sales.sales_channel": "채널",
|
|
76
|
-
"search.entityType.sales.sales_credit_memo": "신용 메모",
|
|
77
|
-
"search.entityType.sales.sales_credit_memo_line": "대변 메모 라인",
|
|
78
|
-
"search.entityType.sales.sales_delivery_window": "배송 기간",
|
|
79
|
-
"search.entityType.sales.sales_document_address": "문서 주소",
|
|
80
|
-
"search.entityType.sales.sales_document_tag": "문서 태그",
|
|
81
|
-
"search.entityType.sales.sales_invoice": "송장",
|
|
82
|
-
"search.entityType.sales.sales_invoice_line": "송장 라인",
|
|
83
|
-
"search.entityType.sales.sales_note": "메모",
|
|
84
|
-
"search.entityType.sales.sales_order": "주문하다",
|
|
85
|
-
"search.entityType.sales.sales_order_adjustment": "주문 조정",
|
|
86
|
-
"search.entityType.sales.sales_order_line": "주문 라인",
|
|
87
|
-
"search.entityType.sales.sales_payment": "지불",
|
|
88
|
-
"search.entityType.sales.sales_payment_allocation": "지불 할당",
|
|
89
|
-
"search.entityType.sales.sales_payment_method": "결제수단",
|
|
90
|
-
"search.entityType.sales.sales_quote": "인용하다",
|
|
91
|
-
"search.entityType.sales.sales_quote_adjustment": "견적 조정",
|
|
92
|
-
"search.entityType.sales.sales_quote_line": "견적 라인",
|
|
93
|
-
"search.entityType.sales.sales_shipment": "선적",
|
|
94
|
-
"search.entityType.sales.sales_shipment_item": "배송 품목",
|
|
95
|
-
"search.entityType.sales.sales_shipping_method": "배송 방법",
|
|
96
|
-
"search.entityType.sales.sales_tax_rate": "세율",
|
|
97
|
-
"search.entityType.staff.staff_team": "팀",
|
|
98
|
-
"search.entityType.staff.staff_team_member": "팀원",
|
|
99
|
-
"search.entityType.staff.staff_team_role": "팀 역할",
|
|
100
|
-
"search.messages.missingConfig": "의미 기반 검색을 사용하려면 임베딩 제공업체를 구성해야 합니다.",
|
|
101
|
-
"search.nav.hybridSearch": "검색",
|
|
102
|
-
"search.scopeHint.currentOrg": "현재 조직으로 범위 지정됨",
|
|
103
|
-
"search.settings.actions.apply": "적용",
|
|
104
|
-
"search.settings.actions.cancel": "취소",
|
|
105
|
-
"search.settings.actions.confirm": "확인",
|
|
106
|
-
"search.settings.actions.loading": "불러오는 중...",
|
|
107
|
-
"search.settings.actions.refresh": "새로고침",
|
|
108
|
-
"search.settings.activity.noLogs": "아직 활동 로그가 없습니다.",
|
|
109
|
-
"search.settings.autoIndexing.description": "새로 생성되거나 업데이트된 레코드를 벡터 검색을 위해 자동으로 인덱싱합니다.",
|
|
110
|
-
"search.settings.autoIndexing.label": "자동 인덱싱 활성화",
|
|
111
|
-
"search.settings.autoIndexing.locked": "환경 변수를 통해 비활성화되었습니다.",
|
|
112
|
-
"search.settings.badge.ready": "준비됨",
|
|
113
|
-
"search.settings.badge.setup": "설정 필요",
|
|
114
|
-
"search.settings.cancelLabel": "취소",
|
|
115
|
-
"search.settings.change.bullet1": "기존 벡터 데이터가 지워집니다",
|
|
116
|
-
"search.settings.change.bullet2": "재인덱싱하는 동안 벡터 검색을 사용할 수 없습니다",
|
|
117
|
-
"search.settings.change.description": "임베딩 제공업체를 변경하려면 모든 데이터를 재인덱싱해야 합니다.",
|
|
118
|
-
"search.settings.change.title": "제공업체 변경 확인",
|
|
119
|
-
"search.settings.clearIndexDescription": "검색 엔진에서 모든 인덱싱된 데이터를 제거합니다.",
|
|
120
|
-
"search.settings.clearIndexDialogDescription": "인덱싱된 모든 데이터가 영구적으로 제거됩니다.",
|
|
121
|
-
"search.settings.clearIndexDialogTitle": "검색 인덱스 지우기",
|
|
122
|
-
"search.settings.clearIndexDialogWarning": "이 작업은 되돌릴 수 없습니다.",
|
|
123
|
-
"search.settings.clearIndexLabel": "인덱스 지우기",
|
|
124
|
-
"search.settings.dimension.label": "차원",
|
|
125
|
-
"search.settings.dimension.mismatch": "불일치",
|
|
126
|
-
"search.settings.documentsLabel": "문서",
|
|
127
|
-
"search.settings.errorLabel": "오류",
|
|
128
|
-
"search.settings.errors.dimensionRequired": "유효한 차원을 입력하세요",
|
|
129
|
-
"search.settings.errors.loadFailed": "설정을 불러오지 못했습니다",
|
|
130
|
-
"search.settings.errors.modelRequired": "모델 이름을 입력하세요",
|
|
131
|
-
"search.settings.errors.reindexFailed": "재인덱싱에 실패했습니다",
|
|
132
|
-
"search.settings.errors.saveFailed": "설정 저장에 실패했습니다",
|
|
133
|
-
"search.settings.fullReindexDescription": "전체 검색 인덱스를 처음부터 다시 빌드합니다.",
|
|
134
|
-
"search.settings.fullReindexDialogDescription": "모든 검색 인덱스를 다시 빌드합니다.",
|
|
135
|
-
"search.settings.fullReindexDialogTitle": "전체 재인덱싱",
|
|
136
|
-
"search.settings.fullReindexDialogWarning": "대용량 데이터셋의 경우 시간이 걸릴 수 있습니다.",
|
|
137
|
-
"search.settings.fullReindexLabel": "전체 재인덱싱",
|
|
138
|
-
"search.settings.fulltext.currentValue": "현재 값",
|
|
139
|
-
"search.settings.fulltext.defaultValue": "기본값",
|
|
140
|
-
"search.settings.fulltext.driver": "드라이버",
|
|
141
|
-
"search.settings.fulltext.envMissing": "환경 변수가 설정되지 않았습니다.",
|
|
142
|
-
"search.settings.fulltext.envSet": "환경 변수가 설정되어 있습니다.",
|
|
143
|
-
"search.settings.fulltext.envVars": "환경 변수",
|
|
144
|
-
"search.settings.fulltext.howTo": "구성 방법",
|
|
145
|
-
"search.settings.fulltext.howToDescription": "전체 텍스트 검색을 활성화하려면 필요한 환경 변수를 설정하세요.",
|
|
146
|
-
"search.settings.fulltext.learnMore": "더 알아보기",
|
|
147
|
-
"search.settings.fulltext.noDriver": "구성된 전체 텍스트 드라이버가 없습니다.",
|
|
148
|
-
"search.settings.fulltext.notReady": "준비되지 않음",
|
|
149
|
-
"search.settings.fulltext.optional": "선택 사항",
|
|
150
|
-
"search.settings.fulltext.ready": "준비됨",
|
|
151
|
-
"search.settings.fulltext.sectionDescription": "Meilisearch로 구동되는 전체 텍스트 검색을 구성합니다.",
|
|
152
|
-
"search.settings.fulltext.sectionTitle": "전체 텍스트 검색",
|
|
153
|
-
"search.settings.fulltextNotConfigured": "전체 텍스트 검색이 구성되지 않았습니다.",
|
|
154
|
-
"search.settings.fulltextNotConfiguredHint": "전체 텍스트 검색을 활성화하려면 MEILISEARCH_HOST를 설정하세요.",
|
|
155
|
-
"search.settings.globalSearch.description": "전역 검색 대화상자(Cmd+K)를 구동할 전략을 구성합니다.",
|
|
156
|
-
"search.settings.globalSearch.fulltext": "전체 텍스트",
|
|
157
|
-
"search.settings.globalSearch.fulltextDescription": "Meilisearch로 구동되는 오타에 강한 검색입니다.",
|
|
158
|
-
"search.settings.globalSearch.notConfigured": "구성되지 않음",
|
|
159
|
-
"search.settings.globalSearch.saveError": "전역 검색 설정 저장에 실패했습니다.",
|
|
160
|
-
"search.settings.globalSearch.title": "전역 검색",
|
|
161
|
-
"search.settings.globalSearch.tokens": "토큰",
|
|
162
|
-
"search.settings.globalSearch.tokensDescription": "PostgreSQL을 사용한 키워드 기반 검색입니다.",
|
|
163
|
-
"search.settings.globalSearch.vector": "벡터",
|
|
164
|
-
"search.settings.globalSearch.vectorDescription": "임베딩을 사용한 AI 기반 의미 기반 검색입니다.",
|
|
165
|
-
"search.settings.loading": "불러오는 중...",
|
|
166
|
-
"search.settings.loadingLabel": "불러오는 중…",
|
|
167
|
-
"search.settings.messages.providerSaved": "임베딩 제공업체가 저장되었습니다",
|
|
168
|
-
"search.settings.messages.reindexStarted": "재인덱싱이 시작되었습니다",
|
|
169
|
-
"search.settings.messages.saved": "설정이 저장되었습니다",
|
|
170
|
-
"search.settings.model.custom": "커스텀...",
|
|
171
|
-
"search.settings.model.label": "모델",
|
|
172
|
-
"search.settings.model.namePlaceholder": "모델 이름",
|
|
173
|
-
"search.settings.noIndexMessage": "아직 생성된 검색 인덱스가 없습니다.",
|
|
174
|
-
"search.settings.pageDescription": "검색 전략을 구성하고, 인덱스를 관리하고, 활동을 모니터링합니다.",
|
|
175
|
-
"search.settings.pageTitle": "검색 설정",
|
|
176
|
-
"search.settings.processingLabel": "처리 중…",
|
|
177
|
-
"search.settings.provider.label": "제공업체",
|
|
178
|
-
"search.settings.provider.notConfigured": "구성되지 않음",
|
|
179
|
-
"search.settings.provider.title": "임베딩 제공업체",
|
|
180
|
-
"search.settings.recreateIndexDescription": "검색 인덱스 구조를 삭제하고 다시 생성합니다.",
|
|
181
|
-
"search.settings.recreateIndexDialogDescription": "검색 인덱스를 삭제하고 다시 생성합니다.",
|
|
182
|
-
"search.settings.recreateIndexDialogTitle": "검색 인덱스 재생성",
|
|
183
|
-
"search.settings.recreateIndexDialogWarning": "인덱싱된 모든 데이터가 손실됩니다.",
|
|
184
|
-
"search.settings.recreateIndexLabel": "인덱스 재생성",
|
|
185
|
-
"search.settings.refreshLabel": "새로고침",
|
|
186
|
-
"search.settings.reindex.button": "재인덱싱 시작",
|
|
187
|
-
"search.settings.reindex.confirmButton": "재인덱싱 시작",
|
|
188
|
-
"search.settings.reindex.confirmDescription": "모든 벡터 임베딩을 다시 빌드합니다. 기존 데이터가 먼저 삭제됩니다.",
|
|
189
|
-
"search.settings.reindex.confirmTitle": "재인덱싱 확인",
|
|
190
|
-
"search.settings.reindex.description": "인덱싱된 모든 엔터티에 대해 벡터 임베딩을 다시 빌드합니다.",
|
|
191
|
-
"search.settings.reindex.running": "재인덱싱 중...",
|
|
192
|
-
"search.settings.reindex.title": "데이터 재인덱싱",
|
|
193
|
-
"search.settings.reindex.warning": "대용량 데이터셋의 경우 시간이 걸릴 수 있습니다.",
|
|
194
|
-
"search.settings.reindexErrorLabel": "재인덱싱 실패",
|
|
195
|
-
"search.settings.reindexInProgress": "재인덱싱 진행 중",
|
|
196
|
-
"search.settings.reindexInProgressDetails": "현재 재인덱싱 작업이 실행 중입니다. 완료될 때까지 기다려 주세요.",
|
|
197
|
-
"search.settings.reindexSuccessLabel": "재인덱싱 완료",
|
|
198
|
-
"search.settings.status.configured": "벡터 검색이 구성됨",
|
|
199
|
-
"search.settings.status.notConfigured": "벡터 검색이 구성되지 않음",
|
|
200
|
-
"search.settings.status.title": "자동 인덱싱",
|
|
201
|
-
"search.settings.tabs.activity": "활동",
|
|
202
|
-
"search.settings.tabs.configuration": "구성",
|
|
203
|
-
"search.settings.tabs.indexManagement": "인덱스 관리",
|
|
204
|
-
"search.settings.title": "검색 설정",
|
|
205
|
-
"search.settings.vector.active": "활성",
|
|
206
|
-
"search.settings.vector.comingSoon": "출시 예정",
|
|
207
|
-
"search.settings.vector.howTo": "구성 방법",
|
|
208
|
-
"search.settings.vector.howToDescription": "벡터 검색을 활성화하려면 임베딩 제공업체를 설정하세요.",
|
|
209
|
-
"search.settings.vector.inheritingDefault": "이 테넌트는 인스턴스/환경 기본값을 상속받고 있습니다. 제공자를 저장하면 이 테넌트에 대한 오버라이드가 생성됩니다.",
|
|
210
|
-
"search.settings.vector.modelsAvailable": "사용 가능한 모델",
|
|
211
|
-
"search.settings.vector.providers": "제공업체",
|
|
212
|
-
"search.settings.vector.providersHint": "환경 변수를 통해 임베딩 제공업체를 구성하세요.",
|
|
213
|
-
"search.settings.vector.sectionDescription": "벡터 임베딩을 사용한 AI 기반 의미 기반 검색을 구성합니다.",
|
|
214
|
-
"search.settings.vector.sectionTitle": "벡터 검색",
|
|
215
|
-
"search.settings.vector.setEnvVar": "환경 변수 설정",
|
|
216
|
-
"search.settings.vector.store": "벡터 저장소",
|
|
217
|
-
"search.settings.vector.storeUnavailable": "벡터 저장소를 사용할 수 없습니다",
|
|
218
|
-
"search.settings.vector.storeUnavailableHint": "저장소에 연결할 수 있을 때까지 벡터 인덱싱과 의미 기반 검색은 건너뜁니다. PostgreSQL 서버에 pgvector 확장을 설치하세요. 상태는 약 1분마다 다시 확인됩니다.",
|
|
219
|
-
"search.settings.vector.usingTenantConfig": "이 테넌트는 자체 임베딩 설정을 사용하고 있습니다.",
|
|
220
|
-
"search.settings.vectorDocumentsLabel": "벡터 문서",
|
|
221
|
-
"search.settings.vectorNotConfigured": "벡터 검색이 구성되지 않았습니다.",
|
|
222
|
-
"search.settings.vectorNotConfiguredHint": "벡터 검색을 활성화하려면 임베딩 제공업체 API 키를 설정하세요.",
|
|
223
|
-
"search.settings.vectorReindex.button": "벡터 재인덱싱 시작",
|
|
224
|
-
"search.settings.vectorReindex.description": "원본 데이터로부터 모든 벡터 임베딩을 다시 빌드합니다.",
|
|
225
|
-
"search.settings.vectorReindex.running": "벡터 재인덱싱 진행 중…",
|
|
226
|
-
"search.settings.vectorReindex.title": "벡터 재인덱싱",
|
|
227
|
-
"search.settings.vectorReindex.warning": "기존 임베딩을 삭제하고 다시 생성합니다.",
|
|
228
|
-
"search.table.actions.open": "열기",
|
|
229
|
-
"search.table.columns.result": "결과",
|
|
230
|
-
"search.table.columns.score": "점수",
|
|
231
|
-
"search.table.columns.source": "소스",
|
|
232
|
-
"search.table.errors.noSources": "사용 가능한 검색 소스가 없습니다.",
|
|
233
|
-
"search.table.errors.searchFailed": "검색에 실패했습니다.",
|
|
234
|
-
"search.table.searchPlaceholder": "검색…",
|
|
235
|
-
"search.table.sources": "소스",
|
|
236
|
-
"search.table.stats.results": "결과",
|
|
237
|
-
"search.table.stats.sources": "소스",
|
|
238
|
-
"search.table.title": "검색 결과"
|
|
239
|
-
}
|