@@ -6,12 +6,13 @@ import { PackageManagerSource } from "../../../../src/services/package-manager/t
66import { Command , CommandEmpty , CommandGroup , CommandInput , CommandItem , CommandList } from "cmdk"
77import { PackageManagerItemCard } from "./components/PackageManagerItemCard"
88import { useStateManager } from "./useStateManager"
9+ import { useAppTranslation } from "@/i18n/TranslationContext"
910
1011interface PackageManagerViewProps {
1112 onDone ?: ( ) => void
1213}
13-
1414const PackageManagerView : React . FC < PackageManagerViewProps > = ( { onDone } ) => {
15+ const { t } = useAppTranslation ( )
1516 const [ state , manager ] = useStateManager ( )
1617
1718 const [ tagSearch , setTagSearch ] = useState ( "" )
@@ -31,7 +32,7 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
3132 < Tab >
3233 < TabHeader className = "flex justify-between items-center sticky top-0 z-10 bg-vscode-editor-background border-b border-vscode-panel-border" >
3334 < div className = "flex items-center" >
34- < h3 className = "text-vscode-foreground m-0" > Package Manager </ h3 >
35+ < h3 className = "text-vscode-foreground m-0" > { t ( "package-manager:title" ) } </ h3 >
3536 </ div >
3637 < div className = "flex gap-2" >
3738 < Button
@@ -41,7 +42,7 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
4142 "bg-vscode-button-background text-vscode-button-foreground hover:bg-vscode-button-hoverBackground" ,
4243 ) }
4344 onClick = { ( ) => manager . transition ( { type : "SET_ACTIVE_TAB" , payload : { tab : "browse" } } ) } >
44- Browse
45+ { t ( "package-manager:tabs.browse" ) }
4546 </ Button >
4647 < Button
4748 variant = { state . activeTab === "sources" ? "default" : "secondary" }
@@ -50,7 +51,7 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
5051 "bg-vscode-button-background text-vscode-button-foreground hover:bg-vscode-button-hoverBackground" ,
5152 ) }
5253 onClick = { ( ) => manager . transition ( { type : "SET_ACTIVE_TAB" , payload : { tab : "sources" } } ) } >
53- Sources
54+ { t ( "package-manager:tabs.sources" ) }
5455 </ Button >
5556 </ div >
5657 </ TabHeader >
@@ -61,7 +62,7 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
6162 < div className = "mb-4" >
6263 < input
6364 type = "text"
64- placeholder = "Search package manager items..."
65+ placeholder = { t ( " package- manager:filters.search.placeholder" ) }
6566 value = { state . filters . search }
6667 onChange = { ( e ) =>
6768 manager . transition ( {
@@ -75,7 +76,7 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
7576 < div className = "flex flex-wrap justify-between gap-2" >
7677 < div className = "whitespace-nowrap" >
7778 < label htmlFor = "type-filter" className = "mr-2" >
78- Filter by type:
79+ { t ( "package-manager:filters.type.label" ) }
7980 </ label >
8081 < select
8182 id = "type-filter"
@@ -87,16 +88,18 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
8788 } )
8889 }
8990 className = "p-1 bg-vscode-dropdown-background text-vscode-dropdown-foreground border border-vscode-dropdown-border rounded" >
90- < option value = "" > All types</ option >
91- < option value = "mode" > Mode</ option >
92- < option value = "mcp server" > MCP Server</ option >
93- < option value = "prompt" > Prompt</ option >
94- < option value = "package" > Package</ option >
91+ < option value = "" > { t ( "package-manager:filters.type.all" ) } </ option >
92+ < option value = "mode" > { t ( "package-manager:filters.type.mode" ) } </ option >
93+ < option value = "mcp server" >
94+ { t ( "package-manager:filters.type.mcp server" ) }
95+ </ option >
96+ < option value = "prompt" > { t ( "package-manager:filters.type.prompt" ) } </ option >
97+ < option value = "package" > { t ( "package-manager:filters.type.package" ) } </ option >
9598 </ select >
9699 </ div >
97100
98101 < div className = "whitespace-nowrap" >
99- < label className = "mr-2" > Sort by: </ label >
102+ < label className = "mr-2" > { t ( "package-manager:filters.sort.label" ) } </ label >
100103 < select
101104 value = { state . sortConfig . by }
102105 onChange = { ( e ) =>
@@ -106,8 +109,10 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
106109 } )
107110 }
108111 className = "p-1 bg-vscode-dropdown-background text-vscode-dropdown-foreground border border-vscode-dropdown-border rounded mr-2" >
109- < option value = "name" > Name</ option >
110- < option value = "lastUpdated" > Last Updated</ option >
112+ < option value = "name" > { t ( "package-manager:filters.sort.name" ) } </ option >
113+ < option value = "lastUpdated" >
114+ { t ( "package-manager:filters.sort.lastUpdated" ) }
115+ </ option >
111116 </ select >
112117 < button
113118 onClick = { ( ) =>
@@ -130,9 +135,13 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
130135 < div >
131136 < div className = "flex items-center justify-between mb-1" >
132137 < div className = "flex items-center" >
133- < label className = "mr-2" > Filter by tags:</ label >
138+ < label className = "mr-2" >
139+ { t ( "package-manager:filters.tags.label" ) }
140+ </ label >
134141 < span className = "text-xs text-vscode-descriptionForeground" >
135- ({ allTags . length } available)
142+ { t ( "package-manager:filters.tags.available" , {
143+ count : allTags . length ,
144+ } ) }
136145 </ span >
137146 </ div >
138147 { state . filters . tags . length > 0 && (
@@ -144,13 +153,15 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
144153 } )
145154 }
146155 className = "p-1 bg-vscode-button-secondaryBackground text-vscode-button-secondaryForeground rounded text-xs" >
147- Clear tags ({ state . filters . tags . length } )
156+ { t ( "package-manager:filters.tags.clear" , {
157+ count : state . filters . tags . length ,
158+ } ) }
148159 </ button >
149160 ) }
150161 </ div >
151162 < Command className = "rounded-lg border border-vscode-dropdown-border" >
152163 < CommandInput
153- placeholder = "Type to search and select tags..."
164+ placeholder = { t ( "package-manager:filters. tags.placeholder" ) }
154165 value = { tagSearch }
155166 onValueChange = { setTagSearch }
156167 onFocus = { ( ) => setIsTagInputActive ( true ) }
@@ -164,7 +175,7 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
164175 { ( isTagInputActive || tagSearch ) && (
165176 < CommandList className = "max-h-[200px] overflow-y-auto bg-vscode-dropdown-background" >
166177 < CommandEmpty className = "p-2 text-sm text-vscode-descriptionForeground" >
167- No matching tags found
178+ { t ( "package-manager:filters. tags.noResults" ) }
168179 </ CommandEmpty >
169180 < CommandGroup >
170181 { allTags
@@ -222,8 +233,10 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
222233 </ Command >
223234 < div className = "text-xs text-vscode-descriptionForeground mt-1" >
224235 { state . filters . tags . length > 0
225- ? `Showing items with any of the selected tags (${ state . filters . tags . length } selected)`
226- : "Click tags to filter items" }
236+ ? t ( "package-manager:filters.tags.selected" , {
237+ count : state . filters . tags . length ,
238+ } )
239+ : t ( "package-manager:filters.tags.clickToFilter" ) }
227240 </ div >
228241 </ div >
229242 ) }
@@ -243,7 +256,7 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
243256 ) {
244257 return (
245258 < div className = "flex flex-col items-center justify-center h-64 text-vscode-descriptionForeground" >
246- < p > Loading items... </ p >
259+ < p > { t ( "package-manager: items.refresh.refreshing" ) } </ p >
247260 </ div >
248261 )
249262 }
@@ -252,7 +265,7 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
252265 if ( isEmpty ) {
253266 return (
254267 < div className = "flex flex-col items-center justify-center h-64 text-vscode-descriptionForeground" >
255- < p > No package manager items found </ p >
268+ < p > { t ( " package- manager: items.empty.noItems" ) } </ p >
256269 </ div >
257270 )
258271 }
@@ -261,9 +274,7 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
261274 return (
262275 < div >
263276 < p className = "text-vscode-descriptionForeground mb-4" >
264- { state . filters . type || state . filters . search || state . filters . tags . length > 0
265- ? `${ items . length } items found (filtered)`
266- : `${ items . length } ${ items . length === 1 ? "item" : "items" } total` }
277+ { t ( "package-manager:items.count" , { count : items . length } ) }
267278 </ p >
268279 < div className = "grid grid-cols-1 gap-4 pb-4" >
269280 { items . map ( ( item ) => (
@@ -313,48 +324,49 @@ const PackageManagerSourcesConfig: React.FC<PackageManagerSourcesConfigProps> =
313324 onRefreshSource,
314325 onSourcesChange,
315326} ) => {
327+ const { t } = useAppTranslation ( )
316328 const [ newSourceUrl , setNewSourceUrl ] = useState ( "" )
317329 const [ newSourceName , setNewSourceName ] = useState ( "" )
318330 const [ error , setError ] = useState ( "" )
319331
320332 const handleAddSource = ( ) => {
321333 if ( ! newSourceUrl ) {
322- setError ( "URL cannot be empty" )
334+ setError ( t ( "package-manager:sources.errors.emptyUrl" ) )
323335 return
324336 }
325337
326338 try {
327339 new URL ( newSourceUrl )
328340 } catch ( e ) {
329- setError ( "Invalid URL format" )
341+ setError ( t ( "package-manager:sources.errors.invalidUrl" ) )
330342 return
331343 }
332344
333345 const nonVisibleCharRegex = / [ ^ \S ] /
334346 if ( nonVisibleCharRegex . test ( newSourceUrl ) ) {
335- setError ( "URL contains non-visible characters other than spaces" )
347+ setError ( t ( "package-manager:sources.errors.nonVisibleChars" ) )
336348 return
337349 }
338350
339351 if ( ! isValidGitRepositoryUrl ( newSourceUrl ) ) {
340- setError ( "URL must be a valid Git repository URL (e.g., https://github.com/username/repo)" )
352+ setError ( t ( "package-manager:sources.errors.invalidGitUrl" ) )
341353 return
342354 }
343355
344356 const normalizedNewUrl = newSourceUrl . toLowerCase ( ) . replace ( / \s + / g, "" )
345357 if ( sources . some ( ( source ) => source . url . toLowerCase ( ) . replace ( / \s + / g, "" ) === normalizedNewUrl ) ) {
346- setError ( "This URL is already in the list (case and whitespace insensitive match)" )
358+ setError ( t ( "package-manager:sources.errors.duplicateUrl" ) )
347359 return
348360 }
349361
350362 if ( newSourceName ) {
351363 if ( newSourceName . length > 20 ) {
352- setError ( "Name must be 20 characters or less" )
364+ setError ( t ( "package-manager:sources.errors.nameTooLong" ) )
353365 return
354366 }
355367
356368 if ( nonVisibleCharRegex . test ( newSourceName ) ) {
357- setError ( "Name contains non-visible characters other than spaces" )
369+ setError ( t ( "package-manager:sources.errors.nonVisibleCharsName" ) )
358370 return
359371 }
360372
@@ -364,14 +376,14 @@ const PackageManagerSourcesConfig: React.FC<PackageManagerSourcesConfigProps> =
364376 ( source ) => source . name && source . name . toLowerCase ( ) . replace ( / \s + / g, "" ) === normalizedNewName ,
365377 )
366378 ) {
367- setError ( "This name is already in use (case and whitespace insensitive match)" )
379+ setError ( t ( "package-manager:sources.errors.duplicateName" ) )
368380 return
369381 }
370382 }
371383
372384 const MAX_SOURCES = 10
373385 if ( sources . length >= MAX_SOURCES ) {
374- setError ( `Maximum of ${ MAX_SOURCES } sources allowed` )
386+ setError ( t ( "package-manager:sources.errors.maxSources" , { max : MAX_SOURCES } ) )
375387 return
376388 }
377389
@@ -401,18 +413,15 @@ const PackageManagerSourcesConfig: React.FC<PackageManagerSourcesConfigProps> =
401413
402414 return (
403415 < div >
404- < h4 className = "text-vscode-foreground mb-2" > Configure Package Manager Sources</ h4 >
405- < p className = "text-vscode-descriptionForeground mb-4" >
406- Add Git repositories that contain package manager items. These repositories will be fetched when
407- browsing the package manager.
408- </ p >
416+ < h4 className = "text-vscode-foreground mb-2" > { t ( "package-manager:sources.title" ) } </ h4 >
417+ < p className = "text-vscode-descriptionForeground mb-4" > { t ( "package-manager:sources.description" ) } </ p >
409418
410419 < div className = "mb-6" >
411- < h5 className = "text-vscode-foreground mb-2" > Add New Source </ h5 >
420+ < h5 className = "text-vscode-foreground mb-2" > { t ( "package-manager:sources.add.title" ) } </ h5 >
412421 < div className = "flex flex-col gap-2 mb-2" >
413422 < input
414423 type = "text"
415- placeholder = "Git repository URL (e.g., https://github.com/username/repo)"
424+ placeholder = { t ( "package-manager:sources.add.urlPlaceholder" ) }
416425 value = { newSourceUrl }
417426 onChange = { ( e ) => {
418427 setNewSourceUrl ( e . target . value )
@@ -421,12 +430,11 @@ const PackageManagerSourcesConfig: React.FC<PackageManagerSourcesConfigProps> =
421430 className = "p-2 bg-vscode-input-background text-vscode-input-foreground border border-vscode-input-border rounded"
422431 />
423432 < p className = "text-xs text-vscode-descriptionForeground mt-1 mb-2" >
424- Supported formats: HTTPS (https://github.com/username/repo), SSH
425- (
[email protected] :username/repo.git), or Git protocol (git://github.com/username/repo.git)
433+ { t ( "package-manager:sources.add.urlFormats" ) }
426434 </ p >
427435 < input
428436 type = "text"
429- placeholder = "Display name (optional, max 20 chars)"
437+ placeholder = { t ( "package-manager:sources.add.namePlaceholder" ) }
430438 value = { newSourceName }
431439 onChange = { ( e ) => {
432440 setNewSourceName ( e . target . value . slice ( 0 , 20 ) )
@@ -439,15 +447,17 @@ const PackageManagerSourcesConfig: React.FC<PackageManagerSourcesConfigProps> =
439447 { error && < p className = "text-red-500 mb-2" > { error } </ p > }
440448 < Button onClick = { handleAddSource } >
441449 < span className = "codicon codicon-add mr-2" > </ span >
442- Add Source
450+ { t ( "package-manager:sources.add.button" ) }
443451 </ Button >
444452 </ div >
445453 < h5 className = "text-vscode-foreground mb-2" >
446- Current Sources{ " " }
447- < span className = "text-vscode-descriptionForeground text-sm" > ({ sources . length } /10 max)</ span >
454+ { t ( "package-manager:sources.current.title" ) } { " " }
455+ < span className = "text-vscode-descriptionForeground text-sm" >
456+ { t ( "package-manager:sources.current.count" , { current : sources . length , max : 10 } ) }
457+ </ span >
448458 </ h5 >
449459 { sources . length === 0 ? (
450- < p className = "text-vscode-descriptionForeground" > No sources configured. Add a source to get started. </ p >
460+ < p className = "text-vscode-descriptionForeground" > { t ( "package-manager: sources.current.empty" ) } </ p >
451461 ) : (
452462 < div className = "grid grid-cols-1 gap-2" >
453463 { sources . map ( ( source , index ) => (
@@ -477,7 +487,7 @@ const PackageManagerSourcesConfig: React.FC<PackageManagerSourcesConfigProps> =
477487 variant = "ghost"
478488 size = "icon"
479489 onClick = { ( ) => onRefreshSource ( source . url ) }
480- title = "Refresh this source"
490+ title = { t ( "package-manager:sources.current.refresh" ) }
481491 className = "text-vscode-foreground"
482492 disabled = { refreshingUrls . includes ( source . url ) } >
483493 < span
@@ -487,6 +497,7 @@ const PackageManagerSourcesConfig: React.FC<PackageManagerSourcesConfigProps> =
487497 variant = "ghost"
488498 size = "icon"
489499 onClick = { ( ) => handleRemoveSource ( index ) }
500+ title = { t ( "package-manager:sources.current.remove" ) }
490501 className = "text-red-500" >
491502 < span className = "codicon codicon-trash" > </ span >
492503 </ Button >
0 commit comments