mirror of
https://github.com/samanhappy/mcphub.git
synced 2025-12-23 18:29:21 -05:00
feat: add french localization (#337)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
This commit is contained in:
@@ -231,8 +231,8 @@ const CloudServerDetail: React.FC<CloudServerDetailProps> = ({
|
||||
className="w-full border rounded-md px-3 py-2 border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 form-input"
|
||||
>
|
||||
<option value=""></option>
|
||||
<option value="true">True</option>
|
||||
<option value="false">False</option>
|
||||
<option value="true">{t('common.true')}</option>
|
||||
<option value="false">{t('common.false')}</option>
|
||||
</select>
|
||||
);
|
||||
} else if (propSchema.type === 'number' || propSchema.type === 'integer') {
|
||||
|
||||
@@ -306,7 +306,7 @@ const ServerForm = ({ onSubmit, onCancel, initialData = null, modalTitle, formEr
|
||||
onChange={() => updateServerType('stdio')}
|
||||
className="mr-1"
|
||||
/>
|
||||
<label htmlFor="command">STDIO</label>
|
||||
<label htmlFor="command">{t('server.typeStdio')}</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
@@ -318,7 +318,7 @@ const ServerForm = ({ onSubmit, onCancel, initialData = null, modalTitle, formEr
|
||||
onChange={() => updateServerType('sse')}
|
||||
className="mr-1"
|
||||
/>
|
||||
<label htmlFor="url">SSE</label>
|
||||
<label htmlFor="url">{t('server.typeSse')}</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
@@ -330,7 +330,7 @@ const ServerForm = ({ onSubmit, onCancel, initialData = null, modalTitle, formEr
|
||||
onChange={() => updateServerType('streamable-http')}
|
||||
className="mr-1"
|
||||
/>
|
||||
<label htmlFor="streamable-http">Streamable HTTP</label>
|
||||
<label htmlFor="streamable-http">{t('server.typeStreamableHttp')}</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
@@ -342,7 +342,7 @@ const ServerForm = ({ onSubmit, onCancel, initialData = null, modalTitle, formEr
|
||||
onChange={() => updateServerType('openapi')}
|
||||
className="mr-1"
|
||||
/>
|
||||
<label htmlFor="openapi">OpenAPI</label>
|
||||
<label htmlFor="openapi">{t('server.typeOpenapi')}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -500,9 +500,9 @@ const ServerForm = ({ onSubmit, onCancel, initialData = null, modalTitle, formEr
|
||||
}))}
|
||||
className="w-full border rounded px-2 py-1 text-sm focus:outline-none form-input"
|
||||
>
|
||||
<option value="header">Header</option>
|
||||
<option value="query">Query</option>
|
||||
<option value="cookie">Cookie</option>
|
||||
<option value="header">{t('server.openapi.apiKeyInHeader')}</option>
|
||||
<option value="query">{t('server.openapi.apiKeyInQuery')}</option>
|
||||
<option value="cookie">{t('server.openapi.apiKeyInCookie')}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
@@ -537,9 +537,9 @@ const ServerForm = ({ onSubmit, onCancel, initialData = null, modalTitle, formEr
|
||||
}))}
|
||||
className="w-full border rounded px-2 py-1 text-sm focus:outline-none form-input"
|
||||
>
|
||||
<option value="basic">Basic</option>
|
||||
<option value="bearer">Bearer</option>
|
||||
<option value="digest">Digest</option>
|
||||
<option value="basic">{t('server.openapi.httpSchemeBasic')}</option>
|
||||
<option value="bearer">{t('server.openapi.httpSchemeBearer')}</option>
|
||||
<option value="digest">{t('server.openapi.httpSchemeDigest')}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const DiscordIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<svg
|
||||
role="img"
|
||||
@@ -11,7 +13,7 @@ export const DiscordIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => {
|
||||
fill="currentColor"
|
||||
{...props}
|
||||
>
|
||||
<title>Discord</title>
|
||||
<title>{t('common.discord')}</title>
|
||||
<path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const GitHubIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<svg
|
||||
role="img"
|
||||
@@ -11,7 +13,7 @@ export const GitHubIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => {
|
||||
fill="currentColor"
|
||||
{...props}
|
||||
>
|
||||
<title>GitHub</title>
|
||||
<title>{t('common.github')}</title>
|
||||
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const SponsorIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<svg
|
||||
role="img"
|
||||
@@ -11,7 +13,7 @@ export const SponsorIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => {
|
||||
fill="currentColor"
|
||||
{...props}
|
||||
>
|
||||
<title>Sponsor</title>
|
||||
<title>{t('sponsor.label')}</title>
|
||||
<path d="M17.625 1.499c-2.32 0-4.354 1.203-5.625 3.03-1.271-1.827-3.305-3.03-5.625-3.03C3.129 1.499 0 4.253 0 8.249c0 4.275 3.068 7.847 5.828 10.227a33.14 33.14 0 0 0 5.616 3.876l.028.017.008.003-.001.003c.163.085.342.126.521.125.179.001.358-.041.521-.125l-.001-.003.008-.003.028-.017a33.14 33.14 0 0 0 5.616-3.876C20.932 16.096 24 12.524 24 8.249c0-3.996-3.129-6.75-6.375-6.75zm-.919 15.275a30.766 30.766 0 0 1-4.703 3.316l-.004-.002-.004.002a30.955 30.955 0 0 1-4.703-3.316c-2.677-2.307-5.047-5.298-5.047-8.523 0-2.754 2.121-4.5 4.125-4.5 2.06 0 3.914 1.479 4.544 3.684.143.495.596.797 1.086.796.49.001.943-.302 1.085-.796.63-2.205 2.484-3.684 4.544-3.684 2.004 0 4.125 1.746 4.125 4.5 0 3.225-2.37 6.216-5.048 8.523z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const WeChatIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<svg
|
||||
role="img"
|
||||
@@ -11,7 +13,7 @@ export const WeChatIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => {
|
||||
fill="currentColor"
|
||||
{...props}
|
||||
>
|
||||
<title>WeChat</title>
|
||||
<title>{t('common.wechat')}</title>
|
||||
<path d="M8.691 2.188C3.891 2.188 0 5.476 0 9.53c0 2.212 1.17 4.203 3.002 5.55a.59.59 0 0 1 .213.665l-.39 1.48c-.019.07-.048.141-.048.213 0 .163.13.295.29.295a.326.326 0 0 0 .167-.054l1.903-1.114a.864.864 0 0 1 .717-.098 10.16 10.16 0 0 0 2.837.403c.276 0 .543-.027.811-.05-.857-2.578.157-4.972 1.932-6.446 1.703-1.415 3.882-1.98 5.853-1.838-.576-3.583-4.196-6.348-8.596-6.348zM5.785 5.991c.642 0 1.162.529 1.162 1.18a1.17 1.17 0 0 1-1.162 1.178A1.17 1.17 0 0 1 4.623 7.17c0-.651.52-1.18 1.162-1.18zm5.813 0c.642 0 1.162.529 1.162 1.18a1.17 1.17 0 0 1-1.162 1.178 1.17 1.17 0 0 1-1.162-1.178c0-.651.52-1.18 1.162-1.18zm5.34 2.867c-1.797-.052-3.746.512-5.28 1.786-1.72 1.428-2.687 3.72-1.78 6.22.942 2.453 3.666 4.229 6.884 4.229.826 0 1.622-.12 2.361-.336a.722.722 0 0 1 .598.082l1.584.926a.272.272 0 0 0 .14.047c.134 0 .24-.111.24-.247 0-.06-.023-.12-.038-.177l-.327-1.233a.582.582 0 0 1-.023-.156.49.49 0 0 1 .201-.398C23.024 18.48 24 16.82 24 14.98c0-3.21-2.931-5.837-6.656-6.088V8.89c-.135-.01-.27-.027-.407-.03zm-2.53 3.274c.535 0 .969.44.969.982a.976.976 0 0 1-.969.983.976.976 0 0 1-.969-.983c0-.542.434-.982.97-.982zm4.844 0c.535 0 .969.44.969.982a.976.976 0 0 1-.969.983.976.976 0 0 1-.969-.983c0-.542.434-.982.969-.982z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
@@ -10,7 +10,8 @@ const LanguageSwitch: React.FC = () => {
|
||||
// Available languages
|
||||
const availableLanguages = [
|
||||
{ code: 'en', label: 'English' },
|
||||
{ code: 'zh', label: '中文' }
|
||||
{ code: 'zh', label: '中文' },
|
||||
{ code: 'fr', label: 'Français' }
|
||||
];
|
||||
|
||||
// Update current language when it changes
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Check, X } from 'lucide-react';
|
||||
import { cn } from '@/utils/cn';
|
||||
|
||||
@@ -19,6 +20,8 @@ const Toast: React.FC<ToastProps> = ({
|
||||
onClose,
|
||||
visible
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
const timer = setTimeout(() => {
|
||||
@@ -83,7 +86,7 @@ const Toast: React.FC<ToastProps> = ({
|
||||
`hover:bg-${type}-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-${type}-500`
|
||||
)}
|
||||
>
|
||||
<span className="sr-only">Dismiss</span>
|
||||
<span className="sr-only">{t('common.dismiss')}</span>
|
||||
<X className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
// Import shared translations from root locales directory
|
||||
import enTranslation from '../../locales/en.json';
|
||||
import zhTranslation from '../../locales/zh.json';
|
||||
import frTranslation from '../../locales/fr.json';
|
||||
|
||||
i18n
|
||||
// Detect user language
|
||||
@@ -20,6 +21,9 @@ i18n
|
||||
zh: {
|
||||
translation: zhTranslation,
|
||||
},
|
||||
fr: {
|
||||
translation: frTranslation,
|
||||
},
|
||||
},
|
||||
fallbackLng: 'en',
|
||||
debug: process.env.NODE_ENV === 'development',
|
||||
|
||||
Reference in New Issue
Block a user