locale upgrade
This commit is contained in:
@@ -60,7 +60,7 @@ function EditorPageContent() {
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [isCreating, setIsCreating] = useState(!projectId);
|
||||
const [editLocale, setEditLocale] = useState(initialLocale);
|
||||
const [baseTexts, setBaseTexts] = useState<{ title: string; description: string } | null>(null);
|
||||
const [baseTexts, setBaseTexts] = useState<{ title: string; description: string; content: string } | null>(null);
|
||||
const [showPreview, setShowPreview] = useState(false);
|
||||
const [_isTyping, setIsTyping] = useState(false);
|
||||
const [history, setHistory] = useState<typeof formData[]>([]);
|
||||
@@ -96,6 +96,7 @@ function EditorPageContent() {
|
||||
setBaseTexts({
|
||||
title: foundProject.title || "",
|
||||
description: foundProject.description || "",
|
||||
content: foundProject.content || "",
|
||||
});
|
||||
const initialData = {
|
||||
title: foundProject.title || "",
|
||||
@@ -145,19 +146,64 @@ function EditorPageContent() {
|
||||
});
|
||||
if (!response.ok) return;
|
||||
const data = await response.json();
|
||||
const tr = data.translation as { title?: string; description?: string } | null;
|
||||
if (tr?.title && tr?.description) {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
title: tr.title || prev.title,
|
||||
description: tr.description || prev.description,
|
||||
}));
|
||||
const tr = data.translation as { title?: string; description?: string; content?: unknown } | null;
|
||||
const translatedContent = (() => {
|
||||
if (typeof tr?.content === "string") return tr.content;
|
||||
if (tr?.content && typeof tr.content === "object" && "markdown" in tr.content) {
|
||||
const markdown = (tr.content as Record<string, unknown>).markdown;
|
||||
if (typeof markdown === "string") return markdown;
|
||||
}
|
||||
return null;
|
||||
})();
|
||||
|
||||
if (tr?.title || tr?.description || translatedContent !== null) {
|
||||
setFormData((prev) => {
|
||||
const next = {
|
||||
...prev,
|
||||
title: tr?.title || prev.title,
|
||||
description: tr?.description || prev.description,
|
||||
content: translatedContent ?? prev.content,
|
||||
};
|
||||
return next;
|
||||
});
|
||||
if (translatedContent !== null) {
|
||||
shouldUpdateContentRef.current = true;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// ignore translation load failures
|
||||
}
|
||||
}, []);
|
||||
|
||||
const switchLocale = useCallback(
|
||||
(next: string) => {
|
||||
setEditLocale(next);
|
||||
if (projectId) {
|
||||
const newUrl = `/editor?id=${projectId}&locale=${encodeURIComponent(next)}`;
|
||||
window.history.replaceState({}, "", newUrl);
|
||||
}
|
||||
|
||||
if (next === "en" && baseTexts) {
|
||||
setFormData((prev) => {
|
||||
const nextData = {
|
||||
...prev,
|
||||
title: baseTexts.title,
|
||||
description: baseTexts.description,
|
||||
content: baseTexts.content,
|
||||
};
|
||||
return nextData;
|
||||
});
|
||||
shouldUpdateContentRef.current = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (projectId) {
|
||||
loadTranslation(projectId, next);
|
||||
}
|
||||
},
|
||||
[projectId, baseTexts, loadTranslation],
|
||||
);
|
||||
|
||||
// Check authentication and load project
|
||||
useEffect(() => {
|
||||
const init = async () => {
|
||||
@@ -188,6 +234,7 @@ function EditorPageContent() {
|
||||
live: "",
|
||||
image: "",
|
||||
};
|
||||
setBaseTexts({ title: "", description: "", content: "" });
|
||||
setFormData(initialData);
|
||||
setOriginalFormData(initialData);
|
||||
setHistory([initialData]);
|
||||
@@ -240,11 +287,12 @@ function EditorPageContent() {
|
||||
const saveTitle = editLocale === "en" ? formData.title.trim() : (baseTexts?.title || formData.title.trim());
|
||||
const saveDescription =
|
||||
editLocale === "en" ? formData.description.trim() : (baseTexts?.description || formData.description.trim());
|
||||
const saveContent = editLocale === "en" ? formData.content.trim() : (baseTexts?.content || formData.content.trim());
|
||||
|
||||
const saveData = {
|
||||
title: saveTitle,
|
||||
description: saveDescription,
|
||||
content: formData.content.trim(),
|
||||
content: saveContent,
|
||||
category: formData.category,
|
||||
tags: formData.tags,
|
||||
github: formData.github.trim() || null,
|
||||
@@ -302,12 +350,21 @@ function EditorPageContent() {
|
||||
locale: editLocale,
|
||||
title: formData.title.trim(),
|
||||
description: formData.description.trim(),
|
||||
content: formData.content.trim(),
|
||||
}),
|
||||
});
|
||||
} catch {
|
||||
// ignore translation save failures
|
||||
}
|
||||
}
|
||||
|
||||
if (editLocale === "en") {
|
||||
setBaseTexts({
|
||||
title: savedProject.title || "",
|
||||
description: savedProject.description || "",
|
||||
content: savedProject.content || "",
|
||||
});
|
||||
}
|
||||
|
||||
// Update project ID if it was a new project
|
||||
if (!projectId && savedProject.id) {
|
||||
@@ -706,27 +763,40 @@ function EditorPageContent() {
|
||||
<label className="block text-sm font-medium text-stone-300 mb-2">
|
||||
Language
|
||||
</label>
|
||||
<div className="custom-select">
|
||||
<select
|
||||
value={editLocale}
|
||||
onChange={(e) => {
|
||||
const next = e.target.value;
|
||||
setEditLocale(next);
|
||||
if (projectId) {
|
||||
// Update URL for deep-linking and reload translation
|
||||
const newUrl = `/editor?id=${projectId}&locale=${encodeURIComponent(next)}`;
|
||||
window.history.replaceState({}, "", newUrl);
|
||||
loadTranslation(projectId, next);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<option value="en">English (default)</option>
|
||||
<option value="de">Deutsch</option>
|
||||
</select>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="custom-select">
|
||||
<select
|
||||
value={editLocale}
|
||||
onChange={(e) => switchLocale(e.target.value)}
|
||||
>
|
||||
<option value="en">English (default)</option>
|
||||
<option value="de">Deutsch</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="inline-flex rounded-lg overflow-hidden border border-stone-700/40">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => switchLocale("en")}
|
||||
className={`px-3 py-1 text-sm ${
|
||||
editLocale === "en" ? "bg-stone-700 text-white" : "bg-stone-800 text-stone-300 hover:bg-stone-700"
|
||||
}`}
|
||||
>
|
||||
EN
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => switchLocale("de")}
|
||||
className={`px-3 py-1 text-sm ${
|
||||
editLocale === "de" ? "bg-stone-700 text-white" : "bg-stone-800 text-stone-300 hover:bg-stone-700"
|
||||
}`}
|
||||
>
|
||||
DE
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{editLocale !== "en" && (
|
||||
<p className="text-xs text-stone-400 mt-2">
|
||||
Title/description are saved as a translation. Other fields are global.
|
||||
Title, description, and content are saved as a translation. Other fields are global.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user