feat(i18n): Complete localization for Settings, Action Plan, Slides, and Google Ads generators - Added actionPlan and slidesGen translation sections for EN/RU/HE - Localized ActionPlanGenerator with dynamic titles, labels, and quick notes - Localized SlidesGenerator with theme, audience, and animation settings - Added RTL support and text-start alignment for Hebrew - Standardized AI Provider and Model labels across all generators

This commit is contained in:
Gemini AI
2025-12-28 01:46:32 +04:00
Unverified
parent 8733b885d4
commit b859d77307
18 changed files with 2696 additions and 1771 deletions

View File

@@ -7,16 +7,19 @@ import { Input } from "@/components/ui/input";
import useStore from "@/lib/store";
import modelAdapter from "@/lib/services/adapter-instance";
import { Save, Key, Server, Eye, EyeOff } from "lucide-react";
import { translations } from "@/lib/i18n/translations";
export default function SettingsPanel() {
const { apiKeys, setApiKey, selectedProvider, setSelectedProvider, qwenTokens, setQwenTokens } = useStore();
const { language, apiKeys, setApiKey, selectedProvider, setSelectedProvider, qwenTokens, setQwenTokens } = useStore();
const t = translations[language].settings;
const common = translations[language].common;
const [showApiKey, setShowApiKey] = useState<Record<string, boolean>>({});
const [isAuthLoading, setIsAuthLoading] = useState(false);
const handleSave = () => {
if (typeof window !== "undefined") {
localStorage.setItem("promptarch-api-keys", JSON.stringify(apiKeys));
alert("API keys saved successfully!");
alert(t.keysSaved);
}
};
@@ -81,7 +84,7 @@ export default function SettingsPanel() {
} catch (error) {
console.error("Qwen OAuth failed", error);
window.alert(
error instanceof Error ? error.message : "Qwen authentication failed"
error instanceof Error ? error.message : t.qwenAuth + " failed"
);
} finally {
setIsAuthLoading(false);
@@ -95,17 +98,17 @@ export default function SettingsPanel() {
return (
<div className="mx-auto max-w-3xl space-y-4 lg:space-y-6">
<Card>
<CardHeader className="p-4 lg:p-6">
<CardHeader className="p-4 lg:p-6 text-start">
<CardTitle className="flex items-center gap-2 text-base lg:text-lg">
<Key className="h-4 w-4 lg:h-5 lg:w-5" />
API Configuration
{t.apiKeys}
</CardTitle>
<CardDescription className="text-xs lg:text-sm">
Configure API keys for different AI providers
{language === "ru" ? "Настройте ключи API для различных провайдеров ИИ" : language === "he" ? "הגדר מפתחות API עבור ספקי בינה מלאכותית שונים" : "Configure API keys for different AI providers"}
</CardDescription>
</CardHeader>
<CardContent className="space-y-4 lg:space-y-6 p-4 lg:p-6 pt-0 lg:pt-0">
<div className="space-y-2">
<div className="space-y-2 text-start">
<label className="flex items-center gap-2 text-xs lg:text-sm font-medium">
<Server className="h-3.5 w-3.5 lg:h-4 lg:w-4" />
Qwen Code API Key
@@ -113,7 +116,7 @@ export default function SettingsPanel() {
<div className="relative">
<Input
type={showApiKey.qwen ? "text" : "password"}
placeholder="Enter your Qwen API key"
placeholder={t.enterKey("Qwen")}
value={apiKeys.qwen || ""}
onChange={(e) => handleApiKeyChange("qwen", e.target.value)}
className="font-mono text-xs lg:text-sm pr-10"
@@ -134,7 +137,7 @@ export default function SettingsPanel() {
</div>
<div className="flex flex-col sm:flex-row sm:items-center gap-2 lg:gap-4">
<p className="text-[10px] lg:text-xs text-muted-foreground flex-1">
Get API key from{" "}
{t.getApiKey}{" "}
<a
href="https://help.aliyun.com/zh/dashscope/"
target="_blank"
@@ -152,20 +155,20 @@ export default function SettingsPanel() {
disabled={isAuthLoading}
>
{isAuthLoading
? "Signing in..."
? (language === "ru" ? "Вход..." : language === "he" ? "מתחבר..." : "Signing in...")
: qwenTokens
? "Logout from Qwen"
: "Login with Qwen (OAuth)"}
? t.logoutQwen
: t.loginQwen}
</Button>
</div>
{qwenTokens && (
<p className="text-[9px] lg:text-[10px] text-green-600 dark:text-green-400 font-medium">
Authenticated via OAuth (Expires: {new Date(qwenTokens.expiresAt || 0).toLocaleString()})
{t.authenticated} ({t.expires}: {new Date(qwenTokens.expiresAt || 0).toLocaleString()})
</p>
)}
</div>
<div className="space-y-2">
<div className="space-y-2 text-start">
<label className="flex items-center gap-2 text-xs lg:text-sm font-medium">
<Server className="h-3.5 w-3.5 lg:h-4 lg:w-4" />
Ollama Cloud API Key
@@ -173,7 +176,7 @@ export default function SettingsPanel() {
<div className="relative">
<Input
type={showApiKey.ollama ? "text" : "password"}
placeholder="Enter your Ollama API key"
placeholder={t.enterKey("Ollama")}
value={apiKeys.ollama || ""}
onChange={(e) => handleApiKeyChange("ollama", e.target.value)}
className="font-mono text-xs lg:text-sm pr-10"
@@ -193,7 +196,7 @@ export default function SettingsPanel() {
</Button>
</div>
<p className="text-[10px] lg:text-xs text-muted-foreground">
Get API key from{" "}
{t.getApiKey}{" "}
<a
href="https://ollama.com/cloud"
target="_blank"
@@ -205,7 +208,7 @@ export default function SettingsPanel() {
</p>
</div>
<div className="space-y-2">
<div className="space-y-2 text-start">
<label className="flex items-center gap-2 text-xs lg:text-sm font-medium">
<Server className="h-3.5 w-3.5 lg:h-4 lg:w-4" />
Z.AI Plan API Key
@@ -213,7 +216,7 @@ export default function SettingsPanel() {
<div className="relative">
<Input
type={showApiKey.zai ? "text" : "password"}
placeholder="Enter your Z.AI API key"
placeholder={t.enterKey("Z.AI")}
value={apiKeys.zai || ""}
onChange={(e) => handleApiKeyChange("zai", e.target.value)}
className="font-mono text-xs lg:text-sm pr-10"
@@ -233,7 +236,7 @@ export default function SettingsPanel() {
</Button>
</div>
<p className="text-[10px] lg:text-xs text-muted-foreground">
Get API key from{" "}
{t.getApiKey}{" "}
<a
href="https://docs.z.ai"
target="_blank"
@@ -247,16 +250,16 @@ export default function SettingsPanel() {
<Button onClick={handleSave} className="w-full h-9 lg:h-10 text-xs lg:text-sm">
<Save className="mr-1.5 lg:mr-2 h-3.5 w-3.5 lg:h-4 lg:w-4" />
Save API Keys
{t.saveKeys}
</Button>
</CardContent>
</Card>
<Card>
<CardHeader className="p-4 lg:p-6">
<CardTitle className="text-base lg:text-lg">Default Provider</CardTitle>
<CardHeader className="p-4 lg:p-6 text-start">
<CardTitle className="text-base lg:text-lg">{t.defaultProvider}</CardTitle>
<CardDescription className="text-xs lg:text-sm">
Select your preferred AI provider
{t.defaultProviderDesc}
</CardDescription>
</CardHeader>
<CardContent className="space-y-3 lg:space-y-4 p-4 lg:p-6 pt-0 lg:pt-0">
@@ -266,8 +269,8 @@ export default function SettingsPanel() {
key={provider}
onClick={() => setSelectedProvider(provider)}
className={`flex items-center gap-2 lg:gap-3 rounded-lg border p-3 lg:p-4 text-left transition-colors hover:bg-muted/50 ${selectedProvider === provider
? "border-primary bg-primary/5"
: "border-border"
? "border-primary bg-primary/5"
: "border-border"
}`}
>
<div className="flex h-8 w-8 lg:h-10 lg:w-10 items-center justify-center rounded-md bg-primary/10">
@@ -276,9 +279,9 @@ export default function SettingsPanel() {
<div className="flex-1 min-w-0">
<h3 className="font-medium capitalize text-sm lg:text-base">{provider}</h3>
<p className="text-[10px] lg:text-sm text-muted-foreground truncate">
{provider === "qwen" && "Alibaba DashScope API"}
{provider === "ollama" && "Ollama Cloud API"}
{provider === "zai" && "Z.AI Plan API"}
{provider === "qwen" && t.qwenDesc}
{provider === "ollama" && t.ollamaDesc}
{provider === "zai" && t.zaiDesc}
</p>
</div>
{selectedProvider === provider && (
@@ -291,16 +294,16 @@ export default function SettingsPanel() {
</Card>
<Card>
<CardHeader className="p-4 lg:p-6">
<CardTitle className="text-base lg:text-lg">Data Privacy</CardTitle>
<CardHeader className="p-4 lg:p-6 text-start">
<CardTitle className="text-base lg:text-lg">{t.dataPrivacy}</CardTitle>
<CardDescription className="text-xs lg:text-sm">
Your data handling preferences
{language === "ru" ? "Ваши настройки обработки данных" : language === "he" ? "העדפות הטיפול בנתונים שלך" : "Your data handling preferences"}
</CardDescription>
</CardHeader>
<CardContent className="p-4 lg:p-6 pt-0 lg:pt-0">
<div className="rounded-md border bg-muted/30 p-3 lg:p-4">
<div className="rounded-md border bg-muted/30 p-3 lg:p-4 text-start">
<p className="text-xs lg:text-sm">
All API keys are stored locally in your browser. Your prompts are sent directly to the selected AI provider and are not stored by PromptArch.
{t.dataPrivacyDesc}
</p>
</div>
</CardContent>