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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user