mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
fix localization bugs
This commit is contained in:
parent
1853ebaa9c
commit
4f8799f169
6 changed files with 100 additions and 89 deletions
|
|
@ -6,12 +6,13 @@ import { PackageManagerSource } from "../../../../src/services/package-manager/t
|
|||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "cmdk"
|
||||
import { PackageManagerItemCard } from "./components/PackageManagerItemCard"
|
||||
import { useStateManager } from "./useStateManager"
|
||||
import { useAppTranslation } from "@/i18n/TranslationContext"
|
||||
|
||||
interface PackageManagerViewProps {
|
||||
onDone?: () => void
|
||||
}
|
||||
|
||||
const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
|
||||
const { t } = useAppTranslation()
|
||||
const [state, manager] = useStateManager()
|
||||
|
||||
const [tagSearch, setTagSearch] = useState("")
|
||||
|
|
@ -31,7 +32,7 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
|
|||
<Tab>
|
||||
<TabHeader className="flex justify-between items-center sticky top-0 z-10 bg-vscode-editor-background border-b border-vscode-panel-border">
|
||||
<div className="flex items-center">
|
||||
<h3 className="text-vscode-foreground m-0">Package Manager</h3>
|
||||
<h3 className="text-vscode-foreground m-0">{t("package-manager:title")}</h3>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
|
|
@ -41,7 +42,7 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
|
|||
"bg-vscode-button-background text-vscode-button-foreground hover:bg-vscode-button-hoverBackground",
|
||||
)}
|
||||
onClick={() => manager.transition({ type: "SET_ACTIVE_TAB", payload: { tab: "browse" } })}>
|
||||
Browse
|
||||
{t("package-manager:tabs.browse")}
|
||||
</Button>
|
||||
<Button
|
||||
variant={state.activeTab === "sources" ? "default" : "secondary"}
|
||||
|
|
@ -50,7 +51,7 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
|
|||
"bg-vscode-button-background text-vscode-button-foreground hover:bg-vscode-button-hoverBackground",
|
||||
)}
|
||||
onClick={() => manager.transition({ type: "SET_ACTIVE_TAB", payload: { tab: "sources" } })}>
|
||||
Sources
|
||||
{t("package-manager:tabs.sources")}
|
||||
</Button>
|
||||
</div>
|
||||
</TabHeader>
|
||||
|
|
@ -61,7 +62,7 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
|
|||
<div className="mb-4">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search package manager items..."
|
||||
placeholder={t("package-manager:filters.search.placeholder")}
|
||||
value={state.filters.search}
|
||||
onChange={(e) =>
|
||||
manager.transition({
|
||||
|
|
@ -75,7 +76,7 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
|
|||
<div className="flex flex-wrap justify-between gap-2">
|
||||
<div className="whitespace-nowrap">
|
||||
<label htmlFor="type-filter" className="mr-2">
|
||||
Filter by type:
|
||||
{t("package-manager:filters.type.label")}
|
||||
</label>
|
||||
<select
|
||||
id="type-filter"
|
||||
|
|
@ -87,16 +88,18 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
|
|||
})
|
||||
}
|
||||
className="p-1 bg-vscode-dropdown-background text-vscode-dropdown-foreground border border-vscode-dropdown-border rounded">
|
||||
<option value="">All types</option>
|
||||
<option value="mode">Mode</option>
|
||||
<option value="mcp server">MCP Server</option>
|
||||
<option value="prompt">Prompt</option>
|
||||
<option value="package">Package</option>
|
||||
<option value="">{t("package-manager:filters.type.all")}</option>
|
||||
<option value="mode">{t("package-manager:filters.type.mode")}</option>
|
||||
<option value="mcp server">
|
||||
{t("package-manager:filters.type.mcp server")}
|
||||
</option>
|
||||
<option value="prompt">{t("package-manager:filters.type.prompt")}</option>
|
||||
<option value="package">{t("package-manager:filters.type.package")}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="whitespace-nowrap">
|
||||
<label className="mr-2">Sort by:</label>
|
||||
<label className="mr-2">{t("package-manager:filters.sort.label")}</label>
|
||||
<select
|
||||
value={state.sortConfig.by}
|
||||
onChange={(e) =>
|
||||
|
|
@ -106,8 +109,10 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
|
|||
})
|
||||
}
|
||||
className="p-1 bg-vscode-dropdown-background text-vscode-dropdown-foreground border border-vscode-dropdown-border rounded mr-2">
|
||||
<option value="name">Name</option>
|
||||
<option value="lastUpdated">Last Updated</option>
|
||||
<option value="name">{t("package-manager:filters.sort.name")}</option>
|
||||
<option value="lastUpdated">
|
||||
{t("package-manager:filters.sort.lastUpdated")}
|
||||
</option>
|
||||
</select>
|
||||
<button
|
||||
onClick={() =>
|
||||
|
|
@ -130,9 +135,13 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
|
|||
<div>
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<div className="flex items-center">
|
||||
<label className="mr-2">Filter by tags:</label>
|
||||
<label className="mr-2">
|
||||
{t("package-manager:filters.tags.label")}
|
||||
</label>
|
||||
<span className="text-xs text-vscode-descriptionForeground">
|
||||
({allTags.length} available)
|
||||
{t("package-manager:filters.tags.available", {
|
||||
count: allTags.length,
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
{state.filters.tags.length > 0 && (
|
||||
|
|
@ -144,13 +153,15 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
|
|||
})
|
||||
}
|
||||
className="p-1 bg-vscode-button-secondaryBackground text-vscode-button-secondaryForeground rounded text-xs">
|
||||
Clear tags ({state.filters.tags.length})
|
||||
{t("package-manager:filters.tags.clear", {
|
||||
count: state.filters.tags.length,
|
||||
})}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<Command className="rounded-lg border border-vscode-dropdown-border">
|
||||
<CommandInput
|
||||
placeholder="Type to search and select tags..."
|
||||
placeholder={t("package-manager:filters.tags.placeholder")}
|
||||
value={tagSearch}
|
||||
onValueChange={setTagSearch}
|
||||
onFocus={() => setIsTagInputActive(true)}
|
||||
|
|
@ -164,7 +175,7 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
|
|||
{(isTagInputActive || tagSearch) && (
|
||||
<CommandList className="max-h-[200px] overflow-y-auto bg-vscode-dropdown-background">
|
||||
<CommandEmpty className="p-2 text-sm text-vscode-descriptionForeground">
|
||||
No matching tags found
|
||||
{t("package-manager:filters.tags.noResults")}
|
||||
</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{allTags
|
||||
|
|
@ -222,8 +233,10 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
|
|||
</Command>
|
||||
<div className="text-xs text-vscode-descriptionForeground mt-1">
|
||||
{state.filters.tags.length > 0
|
||||
? `Showing items with any of the selected tags (${state.filters.tags.length} selected)`
|
||||
: "Click tags to filter items"}
|
||||
? t("package-manager:filters.tags.selected", {
|
||||
count: state.filters.tags.length,
|
||||
})
|
||||
: t("package-manager:filters.tags.clickToFilter")}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -243,7 +256,7 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
|
|||
) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-64 text-vscode-descriptionForeground">
|
||||
<p>Loading items...</p>
|
||||
<p>{t("package-manager:items.refresh.refreshing")}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -252,7 +265,7 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
|
|||
if (isEmpty) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-64 text-vscode-descriptionForeground">
|
||||
<p>No package manager items found</p>
|
||||
<p>{t("package-manager:items.empty.noItems")}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -261,9 +274,7 @@ const PackageManagerView: React.FC<PackageManagerViewProps> = ({ onDone }) => {
|
|||
return (
|
||||
<div>
|
||||
<p className="text-vscode-descriptionForeground mb-4">
|
||||
{state.filters.type || state.filters.search || state.filters.tags.length > 0
|
||||
? `${items.length} items found (filtered)`
|
||||
: `${items.length} ${items.length === 1 ? "item" : "items"} total`}
|
||||
{t("package-manager:items.count", { count: items.length })}
|
||||
</p>
|
||||
<div className="grid grid-cols-1 gap-4 pb-4">
|
||||
{items.map((item) => (
|
||||
|
|
@ -313,48 +324,49 @@ const PackageManagerSourcesConfig: React.FC<PackageManagerSourcesConfigProps> =
|
|||
onRefreshSource,
|
||||
onSourcesChange,
|
||||
}) => {
|
||||
const { t } = useAppTranslation()
|
||||
const [newSourceUrl, setNewSourceUrl] = useState("")
|
||||
const [newSourceName, setNewSourceName] = useState("")
|
||||
const [error, setError] = useState("")
|
||||
|
||||
const handleAddSource = () => {
|
||||
if (!newSourceUrl) {
|
||||
setError("URL cannot be empty")
|
||||
setError(t("package-manager:sources.errors.emptyUrl"))
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
new URL(newSourceUrl)
|
||||
} catch (e) {
|
||||
setError("Invalid URL format")
|
||||
setError(t("package-manager:sources.errors.invalidUrl"))
|
||||
return
|
||||
}
|
||||
|
||||
const nonVisibleCharRegex = /[^\S ]/
|
||||
if (nonVisibleCharRegex.test(newSourceUrl)) {
|
||||
setError("URL contains non-visible characters other than spaces")
|
||||
setError(t("package-manager:sources.errors.nonVisibleChars"))
|
||||
return
|
||||
}
|
||||
|
||||
if (!isValidGitRepositoryUrl(newSourceUrl)) {
|
||||
setError("URL must be a valid Git repository URL (e.g., https://github.com/username/repo)")
|
||||
setError(t("package-manager:sources.errors.invalidGitUrl"))
|
||||
return
|
||||
}
|
||||
|
||||
const normalizedNewUrl = newSourceUrl.toLowerCase().replace(/\s+/g, "")
|
||||
if (sources.some((source) => source.url.toLowerCase().replace(/\s+/g, "") === normalizedNewUrl)) {
|
||||
setError("This URL is already in the list (case and whitespace insensitive match)")
|
||||
setError(t("package-manager:sources.errors.duplicateUrl"))
|
||||
return
|
||||
}
|
||||
|
||||
if (newSourceName) {
|
||||
if (newSourceName.length > 20) {
|
||||
setError("Name must be 20 characters or less")
|
||||
setError(t("package-manager:sources.errors.nameTooLong"))
|
||||
return
|
||||
}
|
||||
|
||||
if (nonVisibleCharRegex.test(newSourceName)) {
|
||||
setError("Name contains non-visible characters other than spaces")
|
||||
setError(t("package-manager:sources.errors.nonVisibleCharsName"))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -364,14 +376,14 @@ const PackageManagerSourcesConfig: React.FC<PackageManagerSourcesConfigProps> =
|
|||
(source) => source.name && source.name.toLowerCase().replace(/\s+/g, "") === normalizedNewName,
|
||||
)
|
||||
) {
|
||||
setError("This name is already in use (case and whitespace insensitive match)")
|
||||
setError(t("package-manager:sources.errors.duplicateName"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_SOURCES = 10
|
||||
if (sources.length >= MAX_SOURCES) {
|
||||
setError(`Maximum of ${MAX_SOURCES} sources allowed`)
|
||||
setError(t("package-manager:sources.errors.maxSources", { max: MAX_SOURCES }))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -401,18 +413,15 @@ const PackageManagerSourcesConfig: React.FC<PackageManagerSourcesConfigProps> =
|
|||
|
||||
return (
|
||||
<div>
|
||||
<h4 className="text-vscode-foreground mb-2">Configure Package Manager Sources</h4>
|
||||
<p className="text-vscode-descriptionForeground mb-4">
|
||||
Add Git repositories that contain package manager items. These repositories will be fetched when
|
||||
browsing the package manager.
|
||||
</p>
|
||||
<h4 className="text-vscode-foreground mb-2">{t("package-manager:sources.title")}</h4>
|
||||
<p className="text-vscode-descriptionForeground mb-4">{t("package-manager:sources.description")}</p>
|
||||
|
||||
<div className="mb-6">
|
||||
<h5 className="text-vscode-foreground mb-2">Add New Source</h5>
|
||||
<h5 className="text-vscode-foreground mb-2">{t("package-manager:sources.add.title")}</h5>
|
||||
<div className="flex flex-col gap-2 mb-2">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Git repository URL (e.g., https://github.com/username/repo)"
|
||||
placeholder={t("package-manager:sources.add.urlPlaceholder")}
|
||||
value={newSourceUrl}
|
||||
onChange={(e) => {
|
||||
setNewSourceUrl(e.target.value)
|
||||
|
|
@ -421,12 +430,11 @@ const PackageManagerSourcesConfig: React.FC<PackageManagerSourcesConfigProps> =
|
|||
className="p-2 bg-vscode-input-background text-vscode-input-foreground border border-vscode-input-border rounded"
|
||||
/>
|
||||
<p className="text-xs text-vscode-descriptionForeground mt-1 mb-2">
|
||||
Supported formats: HTTPS (https://github.com/username/repo), SSH
|
||||
(git@github.com:username/repo.git), or Git protocol (git://github.com/username/repo.git)
|
||||
{t("package-manager:sources.add.urlFormats")}
|
||||
</p>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Display name (optional, max 20 chars)"
|
||||
placeholder={t("package-manager:sources.add.namePlaceholder")}
|
||||
value={newSourceName}
|
||||
onChange={(e) => {
|
||||
setNewSourceName(e.target.value.slice(0, 20))
|
||||
|
|
@ -439,15 +447,17 @@ const PackageManagerSourcesConfig: React.FC<PackageManagerSourcesConfigProps> =
|
|||
{error && <p className="text-red-500 mb-2">{error}</p>}
|
||||
<Button onClick={handleAddSource}>
|
||||
<span className="codicon codicon-add mr-2"></span>
|
||||
Add Source
|
||||
{t("package-manager:sources.add.button")}
|
||||
</Button>
|
||||
</div>
|
||||
<h5 className="text-vscode-foreground mb-2">
|
||||
Current Sources{" "}
|
||||
<span className="text-vscode-descriptionForeground text-sm">({sources.length}/10 max)</span>
|
||||
{t("package-manager:sources.current.title")}{" "}
|
||||
<span className="text-vscode-descriptionForeground text-sm">
|
||||
{t("package-manager:sources.current.count", { current: sources.length, max: 10 })}
|
||||
</span>
|
||||
</h5>
|
||||
{sources.length === 0 ? (
|
||||
<p className="text-vscode-descriptionForeground">No sources configured. Add a source to get started.</p>
|
||||
<p className="text-vscode-descriptionForeground">{t("package-manager:sources.current.empty")}</p>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 gap-2">
|
||||
{sources.map((source, index) => (
|
||||
|
|
@ -477,7 +487,7 @@ const PackageManagerSourcesConfig: React.FC<PackageManagerSourcesConfigProps> =
|
|||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => onRefreshSource(source.url)}
|
||||
title="Refresh this source"
|
||||
title={t("package-manager:sources.current.refresh")}
|
||||
className="text-vscode-foreground"
|
||||
disabled={refreshingUrls.includes(source.url)}>
|
||||
<span
|
||||
|
|
@ -487,6 +497,7 @@ const PackageManagerSourcesConfig: React.FC<PackageManagerSourcesConfigProps> =
|
|||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => handleRemoveSource(index)}
|
||||
title={t("package-manager:sources.current.remove")}
|
||||
className="text-red-500">
|
||||
<span className="codicon codicon-trash"></span>
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -36,15 +36,15 @@ export const PackageManagerItemCard: React.FC<PackageManagerItemCardProps> = ({
|
|||
const getTypeLabel = (type: string) => {
|
||||
switch (type) {
|
||||
case "mode":
|
||||
return t("package_manager:item_card.type_mode")
|
||||
return t("package-manager:filters.type.mode")
|
||||
case "mcp server":
|
||||
return t("package_manager:item_card.type_mcp_server")
|
||||
return t("package-manager:filters.type.mcp server")
|
||||
case "prompt":
|
||||
return t("package_manager:item_card.type_prompt")
|
||||
return t("package-manager:filters.type.prompt")
|
||||
case "package":
|
||||
return t("package_manager:item_card.type_package")
|
||||
return t("package-manager:filters.type.package")
|
||||
default:
|
||||
return t("package_manager:item_card.type_other")
|
||||
return t("package-manager:filters.type.all")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,7 +113,7 @@ export const PackageManagerItemCard: React.FC<PackageManagerItemCardProps> = ({
|
|||
url: item.authorUrl,
|
||||
})
|
||||
}}>
|
||||
{t("package_manager:item_card.by_author", { author: item.author })}
|
||||
{t("package-manager:items.card.by", { author: item.author })}
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
|
|
@ -125,13 +125,13 @@ export const PackageManagerItemCard: React.FC<PackageManagerItemCardProps> = ({
|
|||
url: item.authorUrl,
|
||||
})
|
||||
}}>
|
||||
{t("package_manager:item_card.authors_profile")}
|
||||
{t("package-manager:items.card.viewSource")}
|
||||
</button>
|
||||
)}
|
||||
</p>
|
||||
) : item.author ? (
|
||||
<p className="text-sm text-vscode-descriptionForeground">
|
||||
{t("package_manager:item_card.by_author", { author: item.author })}
|
||||
{t("package-manager:items.card.by", { author: item.author })}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
|
@ -168,8 +168,8 @@ export const PackageManagerItemCard: React.FC<PackageManagerItemCardProps> = ({
|
|||
}}
|
||||
title={
|
||||
filters.tags.includes(tag)
|
||||
? t("package_manager:item_card.remove_tag_filter", { tag })
|
||||
: t("package_manager:item_card.filter_by_tag", { tag })
|
||||
? t("package-manager:filters.tags.clear", { count: tag })
|
||||
: t("package-manager:filters.tags.clickToFilter")
|
||||
}>
|
||||
{tag}
|
||||
</button>
|
||||
|
|
@ -202,23 +202,21 @@ export const PackageManagerItemCard: React.FC<PackageManagerItemCardProps> = ({
|
|||
aria-label={
|
||||
item.sourceUrl && isValidUrl(item.sourceUrl)
|
||||
? ""
|
||||
: item.sourceName || t("package_manager:item_card.source")
|
||||
: item.sourceName || t("package-manager:items.card.viewSource")
|
||||
}>
|
||||
<span
|
||||
className={`codicon codicon-link-external${!item.sourceUrl || !isValidUrl(item.sourceUrl) ? " mr-2" : ""}`}></span>
|
||||
{(!item.sourceUrl || !isValidUrl(item.sourceUrl)) &&
|
||||
(item.sourceName || t("package_manager:item_card.source"))}
|
||||
(item.sourceName || t("package-manager:items.card.viewSource"))}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{groupedItems && (
|
||||
<ExpandableSection
|
||||
title={t("package_manager:item_card.component_details")}
|
||||
title={t("package-manager:items.card.externalComponents", { count: 0 })}
|
||||
badge={(() => {
|
||||
const matchCount = item.items?.filter((subItem) => subItem.matchInfo?.matched).length ?? 0
|
||||
return matchCount > 0
|
||||
? t("package_manager:item_card.match_count", { count: matchCount })
|
||||
: undefined
|
||||
return matchCount > 0 ? t("package-manager:items.count", { count: matchCount }) : undefined
|
||||
})()}
|
||||
defaultExpanded={item.items?.some((subItem) => subItem.matchInfo?.matched) ?? false}>
|
||||
<div className="space-y-4">
|
||||
|
|
|
|||
|
|
@ -22,15 +22,15 @@ export const TypeGroup: React.FC<TypeGroupProps> = ({ type, items, className })
|
|||
const getTypeLabel = (type: string) => {
|
||||
switch (type) {
|
||||
case "mode":
|
||||
return t("package_manager:type_group.modes")
|
||||
return t("package-manager:type-group.modes")
|
||||
case "mcp server":
|
||||
return t("package_manager:type_group.mcp_servers")
|
||||
return t("package-manager:type-group.mcp-servers")
|
||||
case "prompt":
|
||||
return t("package_manager:type_group.prompts")
|
||||
return t("package-manager:type-group.prompts")
|
||||
case "package":
|
||||
return t("package_manager:type_group.packages")
|
||||
return t("package-manager:type-group.packages")
|
||||
default:
|
||||
return t("package_manager:type_group.generic_type", {
|
||||
return t("package-manager:type-group.generic-type", {
|
||||
type: type.charAt(0).toUpperCase() + type.slice(1),
|
||||
})
|
||||
}
|
||||
|
|
@ -60,7 +60,7 @@ export const TypeGroup: React.FC<TypeGroupProps> = ({ type, items, className })
|
|||
)}
|
||||
{item.matchInfo?.matched && (
|
||||
<span className="ml-2 text-xs bg-vscode-badge-background text-vscode-badge-foreground px-1 py-0.5 rounded">
|
||||
{t("package_manager:type_group.match")}
|
||||
{t("package-manager:type-group.match")}
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ describe("PackageManagerItemCard", () => {
|
|||
expect(screen.getByText("Test Package")).toBeInTheDocument()
|
||||
expect(screen.getByText("A test package")).toBeInTheDocument()
|
||||
expect(screen.getByText("by Test Author")).toBeInTheDocument()
|
||||
expect(screen.getByText("Package")).toBeInTheDocument()
|
||||
expect(screen.getByText(/Package/i)).toBeInTheDocument() // Using case-insensitive regex since translations might vary in case
|
||||
})
|
||||
|
||||
it("should render tags", () => {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ i18next.use(initReactI18next).init({
|
|||
interpolation: {
|
||||
escapeValue: false, // React already escapes by default
|
||||
},
|
||||
defaultNS: "package-manager",
|
||||
ns: ["package-manager"],
|
||||
})
|
||||
|
||||
export function loadTranslations() {
|
||||
|
|
|
|||
|
|
@ -19,25 +19,25 @@ i18next.use(initReactI18next).init({
|
|||
},
|
||||
resources: {
|
||||
en: {
|
||||
package_manager: {
|
||||
"package-manager": {
|
||||
// Type group translations
|
||||
"type_group.mcp_servers": "MCP Servers",
|
||||
"type_group.modes": "Modes",
|
||||
"type_group.prompts": "Prompts",
|
||||
"type_group.packages": "Packages",
|
||||
"type_group.match": "Match",
|
||||
"type_group.generic_type": "{{type}}s",
|
||||
"type-group.mcp-servers": "MCP Servers",
|
||||
"type-group.modes": "Modes",
|
||||
"type-group.prompts": "Prompts",
|
||||
"type-group.packages": "Packages",
|
||||
"type-group.match": "Match",
|
||||
"type-group.generic-type": "{{type}}s",
|
||||
|
||||
// Item card translations
|
||||
"item_card.by_author": "by {{author}}",
|
||||
"item_card.type_package": "Package",
|
||||
"item_card.type_mode": "Mode",
|
||||
"item_card.type_mcp_server": "MCP Server",
|
||||
"item_card.type_prompt": "Prompt",
|
||||
"item_card.source": "Source",
|
||||
"item_card.component_details": "Component Details",
|
||||
"item_card.filter_by_tag": "Filter by tag",
|
||||
"item_card.by": "by",
|
||||
"item-card.by-author": "by {{author}}",
|
||||
"item-card.type-package": "Package",
|
||||
"item-card.type-mode": "Mode",
|
||||
"item-card.type-mcp-server": "MCP Server",
|
||||
"item-card.type-prompt": "Prompt",
|
||||
"item-card.source": "Source",
|
||||
"item-card.component-details": "Component Details",
|
||||
"item-card.filter-by-tag": "Filter by tag",
|
||||
"item-card.by": "by",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue