add multi language support
This commit is contained in:
216
web/src/App.js
216
web/src/App.js
@@ -19,105 +19,135 @@ import UserStatus from "./component/UserStatus";
|
|||||||
import ReviewPage from "./component/ReviewPage";
|
import ReviewPage from "./component/ReviewPage";
|
||||||
import UserProfile from "./component/UserProfile";
|
import UserProfile from "./component/UserProfile";
|
||||||
import FeedbackPage from "./component/FeedbackPage";
|
import FeedbackPage from "./component/FeedbackPage";
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { Tr, langCodeContext, LANG_OPTIONS } from "./translate";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [playingFile, setPlayingFile] = useState({});
|
const [playingFile, setPlayingFile] = useState({});
|
||||||
const [user, setUser] = useState({});
|
const [user, setUser] = useState({});
|
||||||
|
const [langCode, setLangCode] = useState("en_US");
|
||||||
|
|
||||||
|
// select language
|
||||||
|
useEffect(() => {
|
||||||
|
const browserCode = window.navigator.language;
|
||||||
|
for (const key in LANG_OPTIONS) {
|
||||||
|
for (const i in LANG_OPTIONS[key].matches) {
|
||||||
|
const code = LANG_OPTIONS[key].matches[i];
|
||||||
|
if (code === browserCode) {
|
||||||
|
setLangCode(key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// fallback to english
|
||||||
|
setLangCode('en-US');
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="base">
|
<div className="base">
|
||||||
<Router>
|
<langCodeContext.Provider value={{ langCode, setLangCode }}>
|
||||||
<header className="header">
|
<Router>
|
||||||
<h3 className="title">
|
<header className="header">
|
||||||
<img src="favicon.png" alt="logo" className="logo" />
|
<h3 className="title">
|
||||||
<span className="title-text">MSW Open Music Project</span>
|
<img src="favicon.png" alt="logo" className="logo" />
|
||||||
<UserStatus user={user} setUser={setUser} />
|
<span className="title-text">MSW Open Music Project</span>
|
||||||
</h3>
|
<UserStatus user={user} setUser={setUser} />
|
||||||
<nav className="nav">
|
</h3>
|
||||||
<NavLink to="/" className="nav-link">
|
<nav className="nav">
|
||||||
Feeling luckly
|
<NavLink to="/" className="nav-link">
|
||||||
</NavLink>
|
{Tr("Feeling luckly")}
|
||||||
<NavLink to="/files" className="nav-link">
|
</NavLink>
|
||||||
Files
|
<NavLink to="/files" className="nav-link">
|
||||||
</NavLink>
|
{Tr("Files")}
|
||||||
<NavLink to="/folders" className="nav-link">
|
</NavLink>
|
||||||
Folders
|
<NavLink to="/folders" className="nav-link">
|
||||||
</NavLink>
|
{Tr("Folders")}
|
||||||
<NavLink to="/manage" className="nav-link">
|
</NavLink>
|
||||||
Manage
|
<NavLink to="/manage" className="nav-link">
|
||||||
</NavLink>
|
{Tr("Manage")}
|
||||||
</nav>
|
</NavLink>
|
||||||
</header>
|
</nav>
|
||||||
<main>
|
</header>
|
||||||
<Routes>
|
<main>
|
||||||
<Route
|
<Routes>
|
||||||
index
|
<Route
|
||||||
path="/"
|
index
|
||||||
element={<GetRandomFiles setPlayingFile={setPlayingFile} />}
|
path="/"
|
||||||
/>
|
element={<GetRandomFiles setPlayingFile={setPlayingFile} />}
|
||||||
<Route
|
/>
|
||||||
path="/files"
|
<Route
|
||||||
element={<SearchFiles setPlayingFile={setPlayingFile} />}
|
path="/files"
|
||||||
/>
|
element={<SearchFiles setPlayingFile={setPlayingFile} />}
|
||||||
<Route
|
/>
|
||||||
path="/folders"
|
<Route
|
||||||
element={<SearchFolders setPlayingFile={setPlayingFile} />}
|
path="/folders"
|
||||||
/>
|
element={<SearchFolders setPlayingFile={setPlayingFile} />}
|
||||||
<Route
|
/>
|
||||||
path="/folders/:id"
|
<Route
|
||||||
element={<FilesInFolder setPlayingFile={setPlayingFile} />}
|
path="/folders/:id"
|
||||||
/>
|
element={<FilesInFolder setPlayingFile={setPlayingFile} />}
|
||||||
<Route
|
/>
|
||||||
path="/manage"
|
<Route
|
||||||
element={<Manage user={user} setUser={setUser} />}
|
path="/manage"
|
||||||
/>
|
element={
|
||||||
<Route
|
<Manage
|
||||||
path="/manage/feedbacks"
|
user={user}
|
||||||
element={<FeedbackPage user={user} />}
|
setUser={setUser}
|
||||||
/>
|
setLangCode={setLangCode}
|
||||||
<Route
|
/>
|
||||||
path="/manage/login"
|
}
|
||||||
element={<Login user={user} setUser={setUser} />}
|
/>
|
||||||
/>
|
<Route
|
||||||
<Route
|
path="/manage/feedbacks"
|
||||||
path="/manage/register"
|
element={<FeedbackPage user={user} />}
|
||||||
element={<Register user={user} setUser={setUser} />}
|
/>
|
||||||
/>
|
<Route
|
||||||
<Route path="/manage/tags" element={<Tags user={user} />} />
|
path="/manage/login"
|
||||||
<Route path="/manage/tags/:id" element={<EditTag user={user} />} />
|
element={<Login user={user} setUser={setUser} />}
|
||||||
<Route
|
/>
|
||||||
path="/manage/reviews/:id"
|
<Route
|
||||||
element={<EditReview user={user} />}
|
path="/manage/register"
|
||||||
/>
|
element={<Register user={user} setUser={setUser} />}
|
||||||
<Route
|
/>
|
||||||
path="/manage/users"
|
<Route path="/manage/tags" element={<Tags user={user} />} />
|
||||||
element={<ManageUser user={user} setUser={setUser} />}
|
<Route
|
||||||
/>
|
path="/manage/tags/:id"
|
||||||
<Route
|
element={<EditTag user={user} />}
|
||||||
path="/manage/users/:id"
|
/>
|
||||||
element={<UserProfile user={user} setUser={setUser} />}
|
<Route
|
||||||
/>
|
path="/manage/reviews/:id"
|
||||||
<Route
|
element={<EditReview user={user} />}
|
||||||
path="/files/:id"
|
/>
|
||||||
element={<FileInfo setPlayingFile={setPlayingFile} />}
|
<Route
|
||||||
/>
|
path="/manage/users"
|
||||||
<Route
|
element={<ManageUser user={user} setUser={setUser} />}
|
||||||
path="/files/:id/share"
|
/>
|
||||||
element={<Share setPlayingFile={setPlayingFile} />}
|
<Route
|
||||||
/>
|
path="/manage/users/:id"
|
||||||
<Route
|
element={<UserProfile user={user} setUser={setUser} />}
|
||||||
path="/files/:id/review"
|
/>
|
||||||
element={
|
<Route
|
||||||
<ReviewPage user={user} setPlayingFile={setPlayingFile} />
|
path="/files/:id"
|
||||||
}
|
element={<FileInfo setPlayingFile={setPlayingFile} />}
|
||||||
/>
|
/>
|
||||||
</Routes>
|
<Route
|
||||||
</main>
|
path="/files/:id/share"
|
||||||
<AudioPlayer
|
element={<Share setPlayingFile={setPlayingFile} />}
|
||||||
playingFile={playingFile}
|
/>
|
||||||
setPlayingFile={setPlayingFile}
|
<Route
|
||||||
/>
|
path="/files/:id/review"
|
||||||
</Router>
|
element={
|
||||||
|
<ReviewPage user={user} setPlayingFile={setPlayingFile} />
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Routes>
|
||||||
|
</main>
|
||||||
|
<AudioPlayer
|
||||||
|
playingFile={playingFile}
|
||||||
|
setPlayingFile={setPlayingFile}
|
||||||
|
/>
|
||||||
|
</Router>
|
||||||
|
</langCodeContext.Provider>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useNavigate } from "react-router";
|
|||||||
import { CalcReadableFilesizeDetail } from "./Common";
|
import { CalcReadableFilesizeDetail } from "./Common";
|
||||||
import FfmpegConfig from "./FfmpegConfig";
|
import FfmpegConfig from "./FfmpegConfig";
|
||||||
import FileDialog from "./FileDialog";
|
import FileDialog from "./FileDialog";
|
||||||
|
import { Tr } from "../translate";
|
||||||
|
|
||||||
function AudioPlayer(props) {
|
function AudioPlayer(props) {
|
||||||
// props.playingFile
|
// props.playingFile
|
||||||
@@ -67,7 +68,7 @@ function AudioPlayer(props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<footer className="vertical">
|
<footer className="vertical">
|
||||||
<h5>Player status</h5>
|
<h5>{Tr("Player status")}</h5>
|
||||||
{props.playingFile.id && (
|
{props.playingFile.id && (
|
||||||
<span>
|
<span>
|
||||||
<FileDialog
|
<FileDialog
|
||||||
@@ -105,7 +106,7 @@ function AudioPlayer(props) {
|
|||||||
props.setPlayingFile({});
|
props.setPlayingFile({});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Stop
|
{Tr("Stop")}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
@@ -138,7 +139,7 @@ function AudioPlayer(props) {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Stop Timer
|
{Tr("Stop Timer")}
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -149,7 +150,7 @@ function AudioPlayer(props) {
|
|||||||
onChange={(event) => setLoop(event.target.checked)}
|
onChange={(event) => setLoop(event.target.checked)}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
/>
|
/>
|
||||||
<label>Loop</label>
|
<label>{Tr("Loop")}</label>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
@@ -158,7 +159,7 @@ function AudioPlayer(props) {
|
|||||||
onChange={(event) => setRaw(event.target.checked)}
|
onChange={(event) => setRaw(event.target.checked)}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
/>
|
/>
|
||||||
<label>Raw</label>
|
<label>{Tr("Raw")}</label>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{!raw && (
|
{!raw && (
|
||||||
@@ -168,7 +169,7 @@ function AudioPlayer(props) {
|
|||||||
onChange={(event) => setPrepare(event.target.checked)}
|
onChange={(event) => setPrepare(event.target.checked)}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
/>
|
/>
|
||||||
<label>Prepare</label>
|
<label>{Tr("Prepare")}</label>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect, useContext } from "react";
|
||||||
|
import { Tr, tr, langCodeContext } from "../translate";
|
||||||
|
|
||||||
function Database() {
|
function Database() {
|
||||||
const [walkPath, setWalkPath] = useState("");
|
const [walkPath, setWalkPath] = useState("");
|
||||||
const [patternString, setPatternString] = useState("wav flac mp3 ogg m4a mka");
|
const [patternString, setPatternString] = useState(
|
||||||
|
"wav flac mp3 ogg m4a mka"
|
||||||
|
);
|
||||||
const [tags, setTags] = useState([]);
|
const [tags, setTags] = useState([]);
|
||||||
const [selectedTags, setSelectedTags] = useState([]);
|
const [selectedTags, setSelectedTags] = useState([]);
|
||||||
const [updating, setUpdating] = useState(false);
|
const [updating, setUpdating] = useState(false);
|
||||||
|
const { langCode } = useContext(langCodeContext);
|
||||||
|
|
||||||
function getTags() {
|
function getTags() {
|
||||||
fetch("/api/v1/get_tags")
|
fetch("/api/v1/get_tags")
|
||||||
@@ -60,21 +64,21 @@ function Database() {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h3>Update Database</h3>
|
<h3>{Tr("Update Database")}</h3>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={walkPath}
|
value={walkPath}
|
||||||
placeholder="walk path"
|
placeholder={tr("walk path", langCode)}
|
||||||
onChange={(e) => setWalkPath(e.target.value)}
|
onChange={(e) => setWalkPath(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={patternString}
|
value={patternString}
|
||||||
placeholder="pattern wav flac mp3"
|
placeholder={tr("pattern wav flac mp3", langCode)}
|
||||||
onChange={(e) => setPatternString(e.target.value)}
|
onChange={(e) => setPatternString(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<h4>Tags</h4>
|
<h4>{Tr("Tags")}</h4>
|
||||||
{tags.map((tag) => (
|
{tags.map((tag) => (
|
||||||
<div key={tag.id}>
|
<div key={tag.id}>
|
||||||
<input
|
<input
|
||||||
@@ -101,7 +105,7 @@ function Database() {
|
|||||||
}}
|
}}
|
||||||
disabled={updating}
|
disabled={updating}
|
||||||
>
|
>
|
||||||
{updating ? "Updating..." : "Update Database"}
|
{updating ? Tr("Updating...") : Tr("Update Database")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
import { useParams, useNavigate } from "react-router";
|
import { useParams, useNavigate } from "react-router";
|
||||||
|
import { tr, Tr, langCodeContext } from "../translate";
|
||||||
|
|
||||||
function SingleReview() {
|
function SingleReview() {
|
||||||
let params = useParams();
|
let params = useParams();
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
|
const { langCode } = useContext(langCodeContext)
|
||||||
|
|
||||||
const [review, setReview] = useState({
|
const [review, setReview] = useState({
|
||||||
id: "",
|
id: "",
|
||||||
@@ -50,7 +52,7 @@ function SingleReview() {
|
|||||||
if (data.error) {
|
if (data.error) {
|
||||||
alert(data.error);
|
alert(data.error);
|
||||||
} else {
|
} else {
|
||||||
alert("Review updated!");
|
alert(tr("Review updated", langCode));
|
||||||
navigate(-1);
|
navigate(-1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -71,7 +73,7 @@ function SingleReview() {
|
|||||||
if (data.error) {
|
if (data.error) {
|
||||||
alert(data.error);
|
alert(data.error);
|
||||||
} else {
|
} else {
|
||||||
alert("Review deleted!");
|
alert(tr("Review deleted", langCode));
|
||||||
navigate(-1);
|
navigate(-1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -83,14 +85,14 @@ function SingleReview() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<h3>Edit Review</h3>
|
<h3>{Tr("Edit Review")}</h3>
|
||||||
<textarea
|
<textarea
|
||||||
value={review.content}
|
value={review.content}
|
||||||
onChange={(e) => setReview({ ...review, content: e.target.value })}
|
onChange={(e) => setReview({ ...review, content: e.target.value })}
|
||||||
></textarea>
|
></textarea>
|
||||||
<div>
|
<div>
|
||||||
<button onClick={() => deleteReview()}>Delete</button>
|
<button onClick={() => deleteReview()}>{Tr("Delete")}</button>
|
||||||
<button onClick={() => save()}>Save</button>
|
<button onClick={() => save()}>{Tr("Save")}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect, useContext } from "react";
|
||||||
import { useParams, useNavigate } from "react-router";
|
import { useParams, useNavigate } from "react-router";
|
||||||
|
import { tr, Tr, langCodeContext } from "../translate";
|
||||||
|
|
||||||
function EditTag() {
|
function EditTag() {
|
||||||
let params = useParams();
|
let params = useParams();
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
|
const { langCode } = useContext(langCodeContext);
|
||||||
|
|
||||||
const [tag, setTag] = useState({
|
const [tag, setTag] = useState({
|
||||||
id: "",
|
id: "",
|
||||||
@@ -54,7 +56,7 @@ function EditTag() {
|
|||||||
if (data.error) {
|
if (data.error) {
|
||||||
alert(data.error);
|
alert(data.error);
|
||||||
} else {
|
} else {
|
||||||
alert("Tag updated successfully");
|
alert(tr("Tag updated successfully", langCode));
|
||||||
refreshTagInfo();
|
refreshTagInfo();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -79,7 +81,7 @@ function EditTag() {
|
|||||||
if (data.error) {
|
if (data.error) {
|
||||||
alert(data.error);
|
alert(data.error);
|
||||||
} else {
|
} else {
|
||||||
alert("Tag deleted successfully");
|
alert(tr("Tag deleted successfully", langCode));
|
||||||
navigate(-1);
|
navigate(-1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -87,9 +89,9 @@ function EditTag() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<h3>Edit Tag</h3>
|
<h3>{Tr("Edit Tag")}</h3>
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="id">ID</label>
|
<label htmlFor="id">{Tr("ID")}</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
disabled
|
disabled
|
||||||
@@ -98,7 +100,7 @@ function EditTag() {
|
|||||||
value={tag.id}
|
value={tag.id}
|
||||||
onChange={(e) => setTag({ ...tag, id: e.target.value })}
|
onChange={(e) => setTag({ ...tag, id: e.target.value })}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="name">Created By</label>
|
<label htmlFor="name">{Tr("Created by")}</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
disabled
|
disabled
|
||||||
@@ -115,7 +117,7 @@ function EditTag() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="name">Name</label>
|
<label htmlFor="name">{Tr("Name")}</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="name"
|
name="name"
|
||||||
@@ -123,15 +125,15 @@ function EditTag() {
|
|||||||
value={tag.name}
|
value={tag.name}
|
||||||
onChange={(e) => setTag({ ...tag, name: e.target.value })}
|
onChange={(e) => setTag({ ...tag, name: e.target.value })}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="description">Description</label>
|
<label htmlFor="description">{Tr("Description")}</label>
|
||||||
<textarea
|
<textarea
|
||||||
name="description"
|
name="description"
|
||||||
id="description"
|
id="description"
|
||||||
value={tag.description}
|
value={tag.description}
|
||||||
onChange={(e) => setTag({ ...tag, description: e.target.value })}
|
onChange={(e) => setTag({ ...tag, description: e.target.value })}
|
||||||
/>
|
/>
|
||||||
<button onClick={deleteTag}>Delete</button>
|
<button onClick={deleteTag}>{Tr("Delete")}</button>
|
||||||
<button onClick={() => updateTagInfo()}>Save</button>
|
<button onClick={() => updateTagInfo()}>{Tr("Save")}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { convertIntToDateTime } from "./Common";
|
import { convertIntToDateTime } from "./Common";
|
||||||
|
import { Tr } from "../translate";
|
||||||
|
|
||||||
function FeedbackPage() {
|
function FeedbackPage() {
|
||||||
const [content, setContext] = useState("");
|
const [content, setContext] = useState("");
|
||||||
@@ -45,17 +46,17 @@ function FeedbackPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<h3>Feedback</h3>
|
<h3>{Tr("Feedbacks")}</h3>
|
||||||
<textarea value={content} onChange={(e) => setContext(e.target.value)} />
|
<textarea value={content} onChange={(e) => setContext(e.target.value)} />
|
||||||
<button onClick={() => submitFeedback()}>Submit</button>
|
<button onClick={() => submitFeedback()}>{Tr("Submit")}</button>
|
||||||
<div>
|
<div>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>User</th>
|
<th>{Tr("User")}</th>
|
||||||
<th>Feedback</th>
|
<th>{Tr("Feedback")}</th>
|
||||||
<th>Date</th>
|
<th>{Tr("Date")}</th>
|
||||||
<th>Action</th>
|
<th>{Tr("Action")}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -90,7 +91,7 @@ function FeedbackPage() {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Delete
|
{Tr("Delete")}
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
|
import { Tr } from "../translate";
|
||||||
|
|
||||||
function FileDialog(props) {
|
function FileDialog(props) {
|
||||||
// props.showStatus
|
// props.showStatus
|
||||||
@@ -23,9 +24,9 @@ function FileDialog(props) {
|
|||||||
{props.file.filename}
|
{props.file.filename}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Play: play using browser player.
|
{Tr("Play: play using browser player.")}
|
||||||
<br />
|
<br />
|
||||||
Info for more actions.
|
{Tr("Info for more actions.")}
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -33,7 +34,7 @@ function FileDialog(props) {
|
|||||||
props.setShowStatus(false);
|
props.setShowStatus(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Info
|
{Tr("Info")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -41,9 +42,9 @@ function FileDialog(props) {
|
|||||||
props.setShowStatus(false);
|
props.setShowStatus(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Play
|
{Tr("Play")}
|
||||||
</button>
|
</button>
|
||||||
<button onClick={() => props.setShowStatus(false)}>Close</button>
|
<button onClick={() => props.setShowStatus(false)}>{Tr("Close")}</button>
|
||||||
</dialog>
|
</dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useNavigate, useParams } from "react-router";
|
import { useNavigate, useParams } from "react-router";
|
||||||
import { useEffect, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
|
import { Tr, tr, langCodeContext } from "../translate";
|
||||||
|
|
||||||
function FileInfo(props) {
|
function FileInfo(props) {
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
@@ -14,6 +15,7 @@ function FileInfo(props) {
|
|||||||
const [tags, setTags] = useState([]);
|
const [tags, setTags] = useState([]);
|
||||||
const [tagsOnFile, setTagsOnFile] = useState([]);
|
const [tagsOnFile, setTagsOnFile] = useState([]);
|
||||||
const [selectedTagID, setSelectedTagID] = useState("");
|
const [selectedTagID, setSelectedTagID] = useState("");
|
||||||
|
const { langCode } = useContext(langCodeContext);
|
||||||
|
|
||||||
function refresh() {
|
function refresh() {
|
||||||
fetch(`/api/v1/get_file_info`, {
|
fetch(`/api/v1/get_file_info`, {
|
||||||
@@ -90,7 +92,9 @@ function FileInfo(props) {
|
|||||||
|
|
||||||
function deleteFile() {
|
function deleteFile() {
|
||||||
// show Warning
|
// show Warning
|
||||||
if (window.confirm("Are you sure you want to delete this file?")) {
|
if (
|
||||||
|
window.confirm(tr("Are you sure you want to delete this file?", langCode))
|
||||||
|
) {
|
||||||
fetch(`/api/v1/delete_file`, {
|
fetch(`/api/v1/delete_file`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -127,7 +131,7 @@ function FileInfo(props) {
|
|||||||
if (data.error) {
|
if (data.error) {
|
||||||
alert(data.error);
|
alert(data.error);
|
||||||
} else {
|
} else {
|
||||||
alert("Filename updated");
|
alert(tr("Filename updated", langCode));
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -163,42 +167,42 @@ function FileInfo(props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<h3>File Details</h3>
|
<h3>{Tr("File Details")}</h3>
|
||||||
<div>
|
<div>
|
||||||
<a href={downloadURL} download>
|
<a href={downloadURL} download>
|
||||||
<button>Download</button>
|
<button>{Tr("Download")}</button>
|
||||||
</a>
|
</a>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.setPlayingFile(file);
|
props.setPlayingFile(file);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Play
|
{Tr("Play")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate(`/files/${params.id}/review`);
|
navigate(`/files/${params.id}/review`);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Review
|
{Tr("Review")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate(`/files/${params.id}/share`);
|
navigate(`/files/${params.id}/share`);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Share
|
{Tr("Share")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
deleteFile();
|
deleteFile();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Delete
|
{Tr("Delete")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="foldername">Folder Name:</label>
|
<label htmlFor="foldername">{Tr("Folder Name")}</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="foldername"
|
id="foldername"
|
||||||
@@ -208,7 +212,7 @@ function FileInfo(props) {
|
|||||||
}}
|
}}
|
||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
<label htmlFor="filename">File Name:</label>
|
<label htmlFor="filename">{Tr("Filename")}</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="filename"
|
id="filename"
|
||||||
@@ -220,15 +224,15 @@ function FileInfo(props) {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="filesize">File Size:</label>
|
<label htmlFor="filesize">{Tr("File size")}</label>
|
||||||
<input type="text" id="filesize" value={file.filesize} readOnly />
|
<input type="text" id="filesize" value={file.filesize} readOnly />
|
||||||
</div>
|
</div>
|
||||||
<div className="horizontal">
|
<div className="horizontal">
|
||||||
<button onClick={updateFilename}>Save</button>
|
<button onClick={updateFilename}>{Tr("Save")}</button>
|
||||||
<button onClick={resetFilename}>Reset</button>
|
<button onClick={resetFilename}>{Tr("Reset")}</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>Tags:</label>
|
<label>{Tr("Tags")}</label>
|
||||||
<ul>
|
<ul>
|
||||||
{tagsOnFile.map((tag) => {
|
{tagsOnFile.map((tag) => {
|
||||||
return (
|
return (
|
||||||
@@ -245,7 +249,7 @@ function FileInfo(props) {
|
|||||||
removeTagOnFile(tag.id);
|
removeTagOnFile(tag.id);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Remove
|
{Tr("Remove")}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
@@ -257,7 +261,7 @@ function FileInfo(props) {
|
|||||||
setSelectedTagID(e.target.value);
|
setSelectedTagID(e.target.value);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<option value="">Select a tag</option>
|
<option value="">{tr("Select a tag", langCode)}</option>
|
||||||
{tags.map((tag) => {
|
{tags.map((tag) => {
|
||||||
return (
|
return (
|
||||||
<option key={tag.id} value={tag.id}>
|
<option key={tag.id} value={tag.id}>
|
||||||
@@ -270,7 +274,7 @@ function FileInfo(props) {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
// check empty
|
// check empty
|
||||||
if (selectedTagID === "") {
|
if (selectedTagID === "") {
|
||||||
alert("Please select a tag");
|
alert(tr("Please select a tag", langCode));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fetch(`/api/v1/put_tag_on_file`, {
|
fetch(`/api/v1/put_tag_on_file`, {
|
||||||
@@ -293,7 +297,7 @@ function FileInfo(props) {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Add Tag
|
{Tr("Add tag")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useState, useEffect } from "react";
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useQuery } from "./Common";
|
import { useQuery } from "./Common";
|
||||||
import FilesTable from "./FilesTable";
|
import FilesTable from "./FilesTable";
|
||||||
|
import { Tr } from "../translate";
|
||||||
|
|
||||||
function FilesInFolder(props) {
|
function FilesInFolder(props) {
|
||||||
let params = useParams();
|
let params = useParams();
|
||||||
@@ -107,13 +108,15 @@ function FilesInFolder(props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<h3>Files in Folder</h3>
|
<h3>{Tr("Files in Folder")}</h3>
|
||||||
<div className="search_toolbar">
|
<div className="search_toolbar">
|
||||||
<button onClick={lastPage}>Last page</button>
|
<button onClick={lastPage}>{Tr("Last page")}</button>
|
||||||
<button disabled>
|
<button disabled>
|
||||||
{isLoading ? "Loading..." : `${offset} - ${offset + files.length}`}
|
{isLoading
|
||||||
|
? Tr("Loading...")
|
||||||
|
: `${offset} - ${offset + files.length}`}
|
||||||
</button>
|
</button>
|
||||||
<button onClick={nextPage}>Next page</button>
|
<button onClick={nextPage}>{Tr("Next page")}</button>
|
||||||
</div>
|
</div>
|
||||||
<FilesTable setPlayingFile={props.setPlayingFile} files={files} />
|
<FilesTable setPlayingFile={props.setPlayingFile} files={files} />
|
||||||
<div>
|
<div>
|
||||||
@@ -123,8 +126,8 @@ function FilesInFolder(props) {
|
|||||||
onChange={(e) => setNewFoldername(e.target.value)}
|
onChange={(e) => setNewFoldername(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<button onClick={() => updateFoldername()}>Save</button>
|
<button onClick={() => updateFoldername()}>{Tr("Save")}</button>
|
||||||
<button onClick={() => resetFoldername()}>Reset</button>
|
<button onClick={() => resetFoldername()}>{Tr("Reset")}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import FileEntry from "./FileEntry";
|
import FileEntry from "./FileEntry";
|
||||||
|
import { Tr } from "../translate";
|
||||||
|
|
||||||
function FilesTable(props) {
|
function FilesTable(props) {
|
||||||
if (props.files.length === 0) {
|
if (props.files.length === 0) {
|
||||||
@@ -8,9 +9,9 @@ function FilesTable(props) {
|
|||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Filename</th>
|
<th>{Tr("Filename")}</th>
|
||||||
<th>Folder Name</th>
|
<th>{Tr("Folder Name")}</th>
|
||||||
<th>Size</th>
|
<th>{Tr("Size")}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
|
import { Tr } from "../translate";
|
||||||
|
|
||||||
function FoldersTable(props) {
|
function FoldersTable(props) {
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
@@ -9,8 +10,8 @@ function FoldersTable(props) {
|
|||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Folder name</th>
|
<th>{Tr("Folder name")}</th>
|
||||||
<th>Action</th>
|
<th>{Tr("Action")}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -23,7 +24,7 @@ function FoldersTable(props) {
|
|||||||
{folder.foldername}
|
{folder.foldername}
|
||||||
</td>
|
</td>
|
||||||
<td onClick={() => navigate(`/folders/${folder.id}`)}>
|
<td onClick={() => navigate(`/folders/${folder.id}`)}>
|
||||||
<button>View</button>
|
<button>{Tr("View")}</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useQuery } from "./Common";
|
import { useQuery } from "./Common";
|
||||||
import FilesTable from "./FilesTable";
|
import FilesTable from "./FilesTable";
|
||||||
|
import { Tr, tr, langCodeContext } from "../translate";
|
||||||
|
|
||||||
function GetRandomFiles(props) {
|
function GetRandomFiles(props) {
|
||||||
const [files, setFiles] = useState([]);
|
const [files, setFiles] = useState([]);
|
||||||
@@ -10,6 +11,7 @@ function GetRandomFiles(props) {
|
|||||||
const navigator = useNavigate();
|
const navigator = useNavigate();
|
||||||
const query = useQuery();
|
const query = useQuery();
|
||||||
const selectedTag = query.get("t") || "";
|
const selectedTag = query.get("t") || "";
|
||||||
|
const { langCode } = useContext(langCodeContext);
|
||||||
|
|
||||||
function getRandomFiles() {
|
function getRandomFiles() {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
@@ -84,7 +86,7 @@ function GetRandomFiles(props) {
|
|||||||
<div className="page">
|
<div className="page">
|
||||||
<div className="search_toolbar">
|
<div className="search_toolbar">
|
||||||
<button className="refresh" onClick={() => refresh(setFiles)}>
|
<button className="refresh" onClick={() => refresh(setFiles)}>
|
||||||
{isLoading ? "Loading..." : "Refresh"}
|
{isLoading ? Tr("Loading...") : Tr("Refresh")}
|
||||||
</button>
|
</button>
|
||||||
<select
|
<select
|
||||||
className="tag_select"
|
className="tag_select"
|
||||||
@@ -93,7 +95,7 @@ function GetRandomFiles(props) {
|
|||||||
}}
|
}}
|
||||||
value={selectedTag}
|
value={selectedTag}
|
||||||
>
|
>
|
||||||
<option value="">All</option>
|
<option value="">{tr("All", langCode)}</option>
|
||||||
{tags.map((tag) => (
|
{tags.map((tag) => (
|
||||||
<option key={tag.id} value={tag.id}>
|
<option key={tag.id} value={tag.id}>
|
||||||
{tag.name}
|
{tag.name}
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useState } from "react";
|
import { useContext, useState } from "react";
|
||||||
|
import { Tr, tr, langCodeContext } from "../translate";
|
||||||
|
|
||||||
function Login(props) {
|
function Login(props) {
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
let [username, setUsername] = useState("");
|
let [username, setUsername] = useState("");
|
||||||
let [password, setPassword] = useState("");
|
let [password, setPassword] = useState("");
|
||||||
|
const { langCode } = useContext(langCodeContext);
|
||||||
|
|
||||||
function login() {
|
function login() {
|
||||||
if (!username || !password) {
|
if (!username || !password) {
|
||||||
alert("Please enter username and password");
|
alert(tr("Please enter username and password", langCode));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fetch("/api/v1/login", {
|
fetch("/api/v1/login", {
|
||||||
@@ -34,15 +36,15 @@ function Login(props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<h2>Login</h2>
|
<h2>{Tr("Login")}</h2>
|
||||||
<label htmlFor="username">Username</label>
|
<label htmlFor="username">{Tr("Username")}</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="username"
|
id="username"
|
||||||
value={username}
|
value={username}
|
||||||
onChange={(e) => setUsername(e.target.value)}
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="password">Password</label>
|
<label htmlFor="password">{Tr("Password")}</label>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="password"
|
id="password"
|
||||||
@@ -56,13 +58,13 @@ function Login(props) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
<button onClick={login}>Login</button>
|
<button onClick={login}>{Tr("Login")}</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate("/manage/register");
|
navigate("/manage/register");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Register
|
{Tr("Register")}
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,32 @@
|
|||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
import Database from "./Database";
|
import Database from "./Database";
|
||||||
|
|
||||||
|
import { Tr, langCodeContext, LANG_OPTIONS } from "../translate";
|
||||||
|
import { useContext } from "react";
|
||||||
|
|
||||||
function Manage(props) {
|
function Manage(props) {
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
|
const { langCode, setLangCode } = useContext(langCodeContext);
|
||||||
|
const codes = Object.keys(LANG_OPTIONS);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<h2>Manage</h2>
|
<h2>{Tr("Manage")}</h2>
|
||||||
<p>Hi, {props.user.username}</p>
|
<p>
|
||||||
|
{Tr("Hi")}, {props.user.username}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<select
|
||||||
|
onChange={(event) => {
|
||||||
|
setLangCode(codes[event.target.selectedIndex]);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{codes.map((code) => {
|
||||||
|
const langOption = LANG_OPTIONS[code];
|
||||||
|
return <option key={code}>{langOption.name}</option>;
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
|
||||||
{props.user.role === 0 && (
|
{props.user.role === 0 && (
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
@@ -15,14 +34,14 @@ function Manage(props) {
|
|||||||
navigate("/manage/login");
|
navigate("/manage/login");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Login
|
{Tr("Login")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate("/manage/register");
|
navigate("/manage/register");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Register
|
{Tr("Register")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -33,7 +52,7 @@ function Manage(props) {
|
|||||||
navigate(`/manage/users/${props.user.id}`);
|
navigate(`/manage/users/${props.user.id}`);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Profile
|
{Tr("Profile")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -48,15 +67,17 @@ function Manage(props) {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Logout
|
{Tr("Logout")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<hr />
|
<hr />
|
||||||
<div className="horizontal">
|
<div className="horizontal">
|
||||||
<button onClick={() => navigate("/manage/tags")}>Tags</button>
|
<button onClick={() => navigate("/manage/tags")}>{Tr("Tags")}</button>
|
||||||
<button onClick={() => navigate("/manage/users")}>Users</button>
|
<button onClick={() => navigate("/manage/users")}>{Tr("Users")}</button>
|
||||||
<button onClick={() => navigate("/manage/feedbacks")}>Feedbacks</button>
|
<button onClick={() => navigate("/manage/feedbacks")}>
|
||||||
|
{Tr("Feedbacks")}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<Database />
|
<Database />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import { Tr } from "../translate";
|
||||||
|
|
||||||
function ManageUser() {
|
function ManageUser() {
|
||||||
const [users, setUsers] = useState([]);
|
const [users, setUsers] = useState([]);
|
||||||
const roleDict = {
|
const roleDict = {
|
||||||
0: "Anonymous",
|
0: "Anonymous",
|
||||||
1: "Admin",
|
1: "Admin",
|
||||||
2: "Normal User",
|
2: "User",
|
||||||
};
|
};
|
||||||
|
|
||||||
function getUsers() {
|
function getUsers() {
|
||||||
@@ -27,13 +28,13 @@ function ManageUser() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<h3>Manage User</h3>
|
<h3>{Tr("Manage User")}</h3>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>{Tr("Name")}</th>
|
||||||
<th>Role</th>
|
<th>{Tr("Role")}</th>
|
||||||
<th>Active</th>
|
<th>{Tr("Active")}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -42,7 +43,7 @@ function ManageUser() {
|
|||||||
<td>
|
<td>
|
||||||
<Link to={`/manage/users/${user.id}`}>@{user.username}</Link>
|
<Link to={`/manage/users/${user.id}`}>@{user.username}</Link>
|
||||||
</td>
|
</td>
|
||||||
<td>{roleDict[user.role]}</td>
|
<td>{Tr(roleDict[user.role])}</td>
|
||||||
<td>
|
<td>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -57,13 +58,15 @@ function ManageUser() {
|
|||||||
id: user.id,
|
id: user.id,
|
||||||
active: e.target.checked,
|
active: e.target.checked,
|
||||||
}),
|
}),
|
||||||
}).then((res) => res.json()).then((data) => {
|
})
|
||||||
if (data.error) {
|
.then((res) => res.json())
|
||||||
alert(data.error);
|
.then((data) => {
|
||||||
} else {
|
if (data.error) {
|
||||||
getUsers();
|
alert(data.error);
|
||||||
}
|
} else {
|
||||||
});
|
getUsers();
|
||||||
|
}
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useState } from "react";
|
import { useContext, useState } from "react";
|
||||||
|
import { tr, Tr, langCodeContext } from "../translate";
|
||||||
|
|
||||||
function Register() {
|
function Register() {
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
@@ -7,12 +8,13 @@ function Register() {
|
|||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
const [password2, setPassword2] = useState("");
|
const [password2, setPassword2] = useState("");
|
||||||
const [role, setRole] = useState("");
|
const [role, setRole] = useState("");
|
||||||
|
const { langCode } = useContext(langCodeContext);
|
||||||
|
|
||||||
function register() {
|
function register() {
|
||||||
if (!username || !password || !password2 || !role) {
|
if (!username || !password || !password2 || !role) {
|
||||||
alert("Please fill out all fields");
|
alert(tr("Please fill out all fields", langCode));
|
||||||
} else if (password !== password2) {
|
} else if (password !== password2) {
|
||||||
alert("Passwords do not match");
|
alert(tr("Password do not match", langCode));
|
||||||
} else {
|
} else {
|
||||||
fetch("/api/v1/register", {
|
fetch("/api/v1/register", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -38,22 +40,22 @@ function Register() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<h2>Register</h2>
|
<h2>{Tr("Register")}</h2>
|
||||||
<label htmlFor="username">Username</label>
|
<label htmlFor="username">{Tr("Username")}</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="username"
|
id="username"
|
||||||
value={username}
|
value={username}
|
||||||
onChange={(e) => setUsername(e.target.value)}
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="password">Password</label>
|
<label htmlFor="password">{Tr("Password")}</label>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="password"
|
id="password"
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="password2">Confirm Password</label>
|
<label htmlFor="password2">{Tr("Confirm Password")}</label>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="password2"
|
id="password2"
|
||||||
@@ -66,13 +68,13 @@ function Register() {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="role">Role</label>
|
<label htmlFor="role">{Tr("Role")}</label>
|
||||||
<select value={role} onChange={(e) => setRole(e.target.value)}>
|
<select value={role} onChange={(e) => setRole(e.target.value)}>
|
||||||
<option value="">Select a role</option>
|
<option value="">{tr("Select a role", langCode)}</option>
|
||||||
<option value="2">User</option>
|
<option value="2">{tr("User", langCode)}</option>
|
||||||
<option value="1">Admin</option>
|
<option value="1">{tr("Admin", langCode)}</option>
|
||||||
</select>
|
</select>
|
||||||
<button onClick={register}>Register</button>
|
<button onClick={register}>{Tr("Register")}</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,28 @@
|
|||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { convertIntToDateTime } from "./Common";
|
import { convertIntToDateTime } from "./Common";
|
||||||
|
import { Tr, tr, langCodeContext } from "../translate";
|
||||||
|
import { useContext } from "react";
|
||||||
|
|
||||||
function ReviewEntry(props) {
|
function ReviewEntry(props) {
|
||||||
|
const { langCode } = useContext(langCodeContext);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h4>
|
<h4>
|
||||||
<Link to={`/manage/users/${props.review.user.id}`}>
|
<Link to={`/manage/users/${props.review.user.id}`}>
|
||||||
@{props.review.user.username}
|
@{props.review.user.username}
|
||||||
</Link>{" "}
|
</Link>{" "}
|
||||||
review{" "}
|
{Tr("review")}{" "}
|
||||||
<Link to={`/files/${props.review.file.id}`}>
|
<Link to={`/files/${props.review.file.id}`}>
|
||||||
{props.review.file.filename}
|
{props.review.file.filename}
|
||||||
</Link>{" "}
|
</Link>{" "}
|
||||||
on {convertIntToDateTime(props.review.created_at)}{" "}
|
{Tr("on")} {convertIntToDateTime(props.review.created_at)}{" "}
|
||||||
{props.review.updated_at !== 0 &&
|
{props.review.updated_at !== 0 &&
|
||||||
"(modified on " +
|
`(${tr("modified on", langCode)} ${convertIntToDateTime(
|
||||||
convertIntToDateTime(props.review.updated_at) +
|
props.review.updated_at
|
||||||
")"}{" "}
|
)} ) `}
|
||||||
{(props.user.role === 1 || props.review.user.id === props.user.id) &&
|
{(props.user.role === 1 || props.review.user.id === props.user.id) &&
|
||||||
props.user.role !== 0 && (
|
props.user.role !== 0 && (
|
||||||
<Link to={`/manage/reviews/${props.review.id}`}>Edit</Link>
|
<Link to={`/manage/reviews/${props.review.id}`}>{Tr("Edit")}</Link>
|
||||||
)}
|
)}
|
||||||
</h4>
|
</h4>
|
||||||
<p>{props.review.content}</p>
|
<p>{props.review.content}</p>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { useParams } from "react-router";
|
import { useParams } from "react-router";
|
||||||
import ReviewEntry from "./ReviewEntry";
|
import ReviewEntry from "./ReviewEntry";
|
||||||
|
import { Tr } from "../translate";
|
||||||
|
|
||||||
function ReviewPage(props) {
|
function ReviewPage(props) {
|
||||||
let params = useParams();
|
let params = useParams();
|
||||||
@@ -55,7 +56,7 @@ function ReviewPage(props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<h3>Review Page</h3>
|
<h3>{Tr("Review Page")}</h3>
|
||||||
<div>
|
<div>
|
||||||
{reviews.map((review) => (
|
{reviews.map((review) => (
|
||||||
<ReviewEntry key={review.id} review={review} user={props.user} />
|
<ReviewEntry key={review.id} review={review} user={props.user} />
|
||||||
@@ -66,7 +67,7 @@ function ReviewPage(props) {
|
|||||||
value={newReview}
|
value={newReview}
|
||||||
onChange={(e) => setNewReview(e.target.value)}
|
onChange={(e) => setNewReview(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<button onClick={() => submitReview()}>Submit</button>
|
<button onClick={() => submitReview()}>{Tr("Submit")}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect, useContext } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useQuery } from "./Common";
|
import { useQuery } from "./Common";
|
||||||
import FilesTable from "./FilesTable";
|
import FilesTable from "./FilesTable";
|
||||||
|
import { Tr, tr, langCodeContext } from "../translate";
|
||||||
|
|
||||||
function SearchFiles(props) {
|
function SearchFiles(props) {
|
||||||
const navigator = useNavigate();
|
const navigator = useNavigate();
|
||||||
@@ -12,6 +13,7 @@ function SearchFiles(props) {
|
|||||||
const offset = parseInt(query.get("o")) || 0;
|
const offset = parseInt(query.get("o")) || 0;
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const limit = 10;
|
const limit = 10;
|
||||||
|
const { langCode } = useContext(langCodeContext);
|
||||||
|
|
||||||
function searchFiles() {
|
function searchFiles() {
|
||||||
// check empty filename
|
// check empty filename
|
||||||
@@ -57,7 +59,7 @@ function SearchFiles(props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<h3>Search Files</h3>
|
<h3>{Tr("Search Files")}</h3>
|
||||||
<div className="search_toolbar">
|
<div className="search_toolbar">
|
||||||
<input
|
<input
|
||||||
onChange={(event) => setFilenameInput(event.target.value)}
|
onChange={(event) => setFilenameInput(event.target.value)}
|
||||||
@@ -67,7 +69,7 @@ function SearchFiles(props) {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Enter filename"
|
placeholder={tr("Enter filename", langCode)}
|
||||||
value={filenameInput}
|
value={filenameInput}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
@@ -75,13 +77,13 @@ function SearchFiles(props) {
|
|||||||
navigator(`/files?q=${filenameInput}&o=0`);
|
navigator(`/files?q=${filenameInput}&o=0`);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isLoading ? "Loading..." : "Search"}
|
{isLoading ? Tr("Loading...") : Tr("Search")}
|
||||||
</button>
|
</button>
|
||||||
<button onClick={lastPage}>Last page</button>
|
<button onClick={lastPage}>{Tr("Last page")}</button>
|
||||||
<button disabled>
|
<button disabled>
|
||||||
{offset} - {offset + files.length}
|
{offset} - {offset + files.length}
|
||||||
</button>
|
</button>
|
||||||
<button onClick={nextPage}>Next page</button>
|
<button onClick={nextPage}>{Tr("Next page")}</button>
|
||||||
</div>
|
</div>
|
||||||
<FilesTable setPlayingFile={props.setPlayingFile} files={files} />
|
<FilesTable setPlayingFile={props.setPlayingFile} files={files} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useQuery } from "./Common";
|
import { useQuery } from "./Common";
|
||||||
import FoldersTable from "./FoldersTable";
|
import FoldersTable from "./FoldersTable";
|
||||||
|
import { Tr, tr, langCodeContext } from "../translate";
|
||||||
|
|
||||||
function SearchFolders() {
|
function SearchFolders() {
|
||||||
const navigator = useNavigate();
|
const navigator = useNavigate();
|
||||||
@@ -12,6 +13,7 @@ function SearchFolders() {
|
|||||||
const offset = parseInt(query.get("o")) || 0;
|
const offset = parseInt(query.get("o")) || 0;
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const limit = 10;
|
const limit = 10;
|
||||||
|
const { langCode } = useContext(langCodeContext);
|
||||||
|
|
||||||
function searchFolder() {
|
function searchFolder() {
|
||||||
if (foldername === "") {
|
if (foldername === "") {
|
||||||
@@ -55,7 +57,7 @@ function SearchFolders() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<h3>Search Folders</h3>
|
<h3>{Tr("Search Folders")}</h3>
|
||||||
<div className="search_toolbar">
|
<div className="search_toolbar">
|
||||||
<input
|
<input
|
||||||
onChange={(event) => setFoldernameInput(event.target.value)}
|
onChange={(event) => setFoldernameInput(event.target.value)}
|
||||||
@@ -65,7 +67,7 @@ function SearchFolders() {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Enter folder name"
|
placeholder={tr("Enter folder name", langCode)}
|
||||||
value={foldernameInput}
|
value={foldernameInput}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
@@ -73,13 +75,13 @@ function SearchFolders() {
|
|||||||
navigator(`/folders?q=${foldernameInput}&o=0`);
|
navigator(`/folders?q=${foldernameInput}&o=0`);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isLoading ? "Loading..." : "Search"}
|
{isLoading ? Tr("Loading...") : Tr("Search")}
|
||||||
</button>
|
</button>
|
||||||
<button onClick={lastPage}>Last page</button>
|
<button onClick={lastPage}>{Tr("Last page")}</button>
|
||||||
<button disabled>
|
<button disabled>
|
||||||
{offset} - {offset + limit}
|
{offset} - {offset + limit}
|
||||||
</button>
|
</button>
|
||||||
<button onClick={nextPage}>Next page</button>
|
<button onClick={nextPage}>{Tr("Next page")}</button>
|
||||||
</div>
|
</div>
|
||||||
<FoldersTable folders={folders} />
|
<FoldersTable folders={folders} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useParams } from "react-router";
|
import { useParams } from "react-router";
|
||||||
import FilesTable from "./FilesTable";
|
import FilesTable from "./FilesTable";
|
||||||
|
import { Tr } from "../translate";
|
||||||
|
|
||||||
function Share(props) {
|
function Share(props) {
|
||||||
let params = useParams();
|
let params = useParams();
|
||||||
@@ -23,13 +24,14 @@ function Share(props) {
|
|||||||
}, [params]);
|
}, [params]);
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<h3>Share with others!</h3>
|
<h3>{Tr("Share with others!")}</h3>
|
||||||
<p>
|
<p>
|
||||||
👇 Click the filename below to enjoy music!
|
{Tr("Share link")}:{" "}
|
||||||
<br />
|
<a href={window.location.href}>{window.location.href}</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Share link: <a href={window.location.href}>{window.location.href}</a>
|
👇 {Tr("Click the filename below to enjoy music!")}
|
||||||
|
<br />
|
||||||
</p>
|
</p>
|
||||||
<FilesTable setPlayingFile={props.setPlayingFile} files={file} />
|
<FilesTable setPlayingFile={props.setPlayingFile} files={file} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import { Tr } from "../translate";
|
||||||
|
|
||||||
function Tags() {
|
function Tags() {
|
||||||
const [tags, setTags] = useState([]);
|
const [tags, setTags] = useState([]);
|
||||||
@@ -25,14 +26,14 @@ function Tags() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<h3>Tags</h3>
|
<h3>{Tr("Tags")}</h3>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>{Tr("Name")}</th>
|
||||||
<th>Description</th>
|
<th>{Tr("Description")}</th>
|
||||||
<th>Created By</th>
|
<th>{Tr("Created by")}</th>
|
||||||
<th>Actions</th>
|
<th>{Tr("Action")}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -46,25 +47,25 @@ function Tags() {
|
|||||||
</Link>
|
</Link>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<Link to={`/manage/tags/${tag.id}`}>Edit</Link>
|
<Link to={`/manage/tags/${tag.id}`}>{Tr("Edit")}</Link>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{!showAddTag && (
|
{!showAddTag && (
|
||||||
<button onClick={() => setShowAddTag(true)}>Add Tag</button>
|
<button onClick={() => setShowAddTag(true)}>{Tr("Add tag")}</button>
|
||||||
)}
|
)}
|
||||||
{showAddTag && (
|
{showAddTag && (
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="newTagName">New Tag Name</label>
|
<label htmlFor="newTagName">{Tr("New Tag Name")}</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="newTagName"
|
id="newTagName"
|
||||||
value={newTagName}
|
value={newTagName}
|
||||||
onChange={(e) => setNewTagName(e.target.value)}
|
onChange={(e) => setNewTagName(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="newTagDescription">New Tag Description</label>
|
<label htmlFor="newTagDescription">{Tr("New Tag Description")}</label>
|
||||||
<textarea
|
<textarea
|
||||||
id="newTagDescription"
|
id="newTagDescription"
|
||||||
value={newTagDescription}
|
value={newTagDescription}
|
||||||
@@ -94,7 +95,7 @@ function Tags() {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Create Tag
|
{Tr("Create tag")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect, useContext } from "react";
|
||||||
import { useParams } from "react-router";
|
import { useParams } from "react-router";
|
||||||
import ReviewEntry from "./ReviewEntry";
|
import ReviewEntry from "./ReviewEntry";
|
||||||
|
import { Tr, tr, langCodeContext } from "../translate";
|
||||||
|
|
||||||
function UserProfile(props) {
|
function UserProfile(props) {
|
||||||
let params = useParams();
|
let params = useParams();
|
||||||
@@ -15,6 +16,7 @@ function UserProfile(props) {
|
|||||||
active: false,
|
active: false,
|
||||||
avatar_id: 0,
|
avatar_id: 0,
|
||||||
});
|
});
|
||||||
|
const { langCode } = useContext(langCodeContext);
|
||||||
|
|
||||||
function getReviews() {
|
function getReviews() {
|
||||||
fetch("/api/v1/get_reviews_by_user", {
|
fetch("/api/v1/get_reviews_by_user", {
|
||||||
@@ -63,7 +65,7 @@ function UserProfile(props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<div className="page">
|
||||||
<h3>User Profile</h3>
|
<h3>{Tr("User Profile")}</h3>
|
||||||
<div className="horizontal">
|
<div className="horizontal">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -103,26 +105,26 @@ function UserProfile(props) {
|
|||||||
}}
|
}}
|
||||||
disabled={props.user.id !== user.id && props.user.role !== 1}
|
disabled={props.user.id !== user.id && props.user.role !== 1}
|
||||||
>
|
>
|
||||||
Save Username
|
{Tr("Save username")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
value={oldPassword}
|
value={oldPassword}
|
||||||
placeholder="Old Password"
|
placeholder={tr("Old password", langCode)}
|
||||||
onChange={(e) => setOldPassword(e.target.value)}
|
onChange={(e) => setOldPassword(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
value={newPassword}
|
value={newPassword}
|
||||||
placeholder="New Password"
|
placeholder={tr("New password", langCode)}
|
||||||
onChange={(e) => setNewPassword(e.target.value)}
|
onChange={(e) => setNewPassword(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
value={newPasswordConfirm}
|
value={newPasswordConfirm}
|
||||||
placeholder="Confirm New Password"
|
placeholder={tr("Confirm new password", langCode)}
|
||||||
onChange={(e) => setNewPasswordConfirm(e.target.value)}
|
onChange={(e) => setNewPasswordConfirm(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
@@ -144,7 +146,7 @@ function UserProfile(props) {
|
|||||||
if (data.error) {
|
if (data.error) {
|
||||||
alert(data.error);
|
alert(data.error);
|
||||||
} else {
|
} else {
|
||||||
alert("Password updated successfully!");
|
alert(tr("Password updated successfully!", langCode));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
@@ -154,10 +156,10 @@ function UserProfile(props) {
|
|||||||
newPassword.length === 0
|
newPassword.length === 0
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Change Password
|
{Tr("Change password")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<h4>Reviews</h4>
|
<h4>{Tr("Reviews")}</h4>
|
||||||
{reviews.map((review) => (
|
{reviews.map((review) => (
|
||||||
<ReviewEntry key={review.id} review={review} user={props.user} />
|
<ReviewEntry key={review.id} review={review} user={props.user} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
44
web/src/translate/index.js
Normal file
44
web/src/translate/index.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { createContext, renderToString } from "react";
|
||||||
|
import MAP_zh_CN from "./zh_CN";
|
||||||
|
|
||||||
|
const LANG_OPTIONS = {
|
||||||
|
"en-US": {
|
||||||
|
name: "English",
|
||||||
|
langMap: {},
|
||||||
|
matches: ["en-US", "en"],
|
||||||
|
},
|
||||||
|
"zh-CN": {
|
||||||
|
name: "中文(简体)",
|
||||||
|
langMap: MAP_zh_CN,
|
||||||
|
matches: ["zh-CN", "zh"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const langCodeContext = createContext("en-US");
|
||||||
|
|
||||||
|
function tr(text, langCode) {
|
||||||
|
const option = LANG_OPTIONS[langCode];
|
||||||
|
if (option === undefined) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
const langMap = LANG_OPTIONS[langCode].langMap;
|
||||||
|
|
||||||
|
const translatedText = langMap[text.toLowerCase()];
|
||||||
|
if (translatedText === undefined) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
return translatedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Tr(text) {
|
||||||
|
return (
|
||||||
|
<langCodeContext.Consumer>
|
||||||
|
{({ langCode }) => {
|
||||||
|
return tr(text, langCode);
|
||||||
|
}}
|
||||||
|
</langCodeContext.Consumer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { tr, Tr, LANG_OPTIONS, langCodeContext };
|
||||||
105
web/src/translate/zh_CN.js
Normal file
105
web/src/translate/zh_CN.js
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
const LANG_zh_CN = {
|
||||||
|
"feeling luckly": "随机",
|
||||||
|
files: "文件",
|
||||||
|
folders: "文件夹",
|
||||||
|
manage: "管理",
|
||||||
|
"manage user": "用户管理",
|
||||||
|
active: "激活",
|
||||||
|
"search files": "搜索文件",
|
||||||
|
"search folders": "搜索文件夹",
|
||||||
|
"enter filename": "输入文件名",
|
||||||
|
"enter folder name": "输入文件名",
|
||||||
|
search: "搜索",
|
||||||
|
"last page": "上一页",
|
||||||
|
all: "全部",
|
||||||
|
"loading...": "加载中...",
|
||||||
|
"next page": "下一页",
|
||||||
|
"search polders": "搜索文件夹",
|
||||||
|
"share with others!": "分享给好友!",
|
||||||
|
"click the filename below to enjoy music!": "点击下面的文件名开始享受音乐!",
|
||||||
|
"share link": "分享链接",
|
||||||
|
hi: "您好",
|
||||||
|
profile: "个人信息",
|
||||||
|
"user profile": "用户信息",
|
||||||
|
"save username": "更改用户名",
|
||||||
|
save: "保存",
|
||||||
|
reset: "重置",
|
||||||
|
"old password": "旧密码",
|
||||||
|
"new password": "新密码",
|
||||||
|
"confirm new password": "确认新密码",
|
||||||
|
"change password": "更改密码",
|
||||||
|
reviews: "评论",
|
||||||
|
review: "评论",
|
||||||
|
on: "在",
|
||||||
|
edit: "编辑",
|
||||||
|
"modified on": "修改于",
|
||||||
|
share: "分享",
|
||||||
|
delete: "删除",
|
||||||
|
remove: "移除",
|
||||||
|
"file details": "文件详情",
|
||||||
|
download: "下载",
|
||||||
|
logout: "登出",
|
||||||
|
tags: "标签",
|
||||||
|
"add tag": "添加标签",
|
||||||
|
"select a tag": "选择一个标签",
|
||||||
|
"review page": "评论页面",
|
||||||
|
submit: "提交",
|
||||||
|
users: "用户",
|
||||||
|
feedbacks: "反馈",
|
||||||
|
feedback: "反馈",
|
||||||
|
date: "时间",
|
||||||
|
action: "操作",
|
||||||
|
"new tag name": "新标签名",
|
||||||
|
"new tag description": "新标签描述",
|
||||||
|
"update database": "更新索引",
|
||||||
|
"updating...": "更新中...",
|
||||||
|
refresh: "刷新",
|
||||||
|
filename: "文件名",
|
||||||
|
"folder name": "文件夹名",
|
||||||
|
size: "大小",
|
||||||
|
"player status": "播放状态",
|
||||||
|
play: "播放",
|
||||||
|
stop: "停止",
|
||||||
|
"stop timer": "定时停止",
|
||||||
|
loop: "循环",
|
||||||
|
raw: "无损",
|
||||||
|
prepare: "预转码",
|
||||||
|
"file size": "文件大小",
|
||||||
|
login: "登陆",
|
||||||
|
register: "注册",
|
||||||
|
"play: play using browser player.": "播放: 使用浏览器播放",
|
||||||
|
"info for more actions.": "详细: 查看更多相关信息",
|
||||||
|
info: "详细",
|
||||||
|
close: "关闭",
|
||||||
|
"please enter username and password": "请输入用户名和密码",
|
||||||
|
username: "用户名",
|
||||||
|
password: "密码",
|
||||||
|
"please fill out all fields": "请完整填写所有信息",
|
||||||
|
"password do not match": "两次密码不一致",
|
||||||
|
"password updated successfully!": "密码已成功更新!",
|
||||||
|
role: "身份",
|
||||||
|
user: "用户",
|
||||||
|
admin: "管理员",
|
||||||
|
anonymous: "匿名",
|
||||||
|
"select a role": "选择身份",
|
||||||
|
"walk path": "遍历目录",
|
||||||
|
"pattern wav flac mp3": "拓展名 wav flac mp3",
|
||||||
|
"review updated": "已修改评论",
|
||||||
|
"review deleted": "已删除评论",
|
||||||
|
"edit review": "编辑评论",
|
||||||
|
view: "查看",
|
||||||
|
"tag updated successfully": "标签修改成功",
|
||||||
|
"tag deleted successfully": "标签删除成功",
|
||||||
|
"edit tag": "编辑标签",
|
||||||
|
id: "编号",
|
||||||
|
"created by": "创建者",
|
||||||
|
"create tag": "创建新标签",
|
||||||
|
name: "名称",
|
||||||
|
description: "描述",
|
||||||
|
"are you sure you want to delete this file?": "你确定要删除这个文件吗?",
|
||||||
|
"filename updated": "已修改文件名",
|
||||||
|
"please select a tag": "请选择一个标签",
|
||||||
|
"files in folder": "文件夹内",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LANG_zh_CN;
|
||||||
Reference in New Issue
Block a user