forked from Kieran/zap.stream
Setup translations
This commit is contained in:
parent
b96002abe9
commit
2754cb1581
@ -78,7 +78,7 @@ export function FileUploader({ defaultImage, onClear, onFileUpload }: FileUpload
|
|||||||
<div className="file-uploader-container">
|
<div className="file-uploader-container">
|
||||||
<label className="file-uploader">
|
<label className="file-uploader">
|
||||||
<input type="file" onChange={onFileChange} />
|
<input type="file" onChange={onFileChange} />
|
||||||
{isUploading ? "Uploading..." : "Add File"}
|
{isUploading ? <FormattedMessage defaultMessage="Uploading..." /> : <FormattedMessage defaultMessage="Add File" />}
|
||||||
</label>
|
</label>
|
||||||
<div className="file-uploader-preview">
|
<div className="file-uploader-preview">
|
||||||
{img?.length > 0 && (
|
{img?.length > 0 && (
|
||||||
|
@ -180,6 +180,7 @@ function CardDialog({ header, cta, cancelCta, card, onSave, onCancel }: CardDial
|
|||||||
const [image, setImage] = useState(card?.image ?? "");
|
const [image, setImage] = useState(card?.image ?? "");
|
||||||
const [content, setContent] = useState(card?.content ?? "");
|
const [content, setContent] = useState(card?.content ?? "");
|
||||||
const [link, setLink] = useState(card?.link ?? "");
|
const [link, setLink] = useState(card?.link ?? "");
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="new-card">
|
<div className="new-card">
|
||||||
@ -195,7 +196,7 @@ function CardDialog({ header, cta, cancelCta, card, onSave, onCancel }: CardDial
|
|||||||
type="text"
|
type="text"
|
||||||
value={title}
|
value={title}
|
||||||
onChange={e => setTitle(e.target.value)}
|
onChange={e => setTitle(e.target.value)}
|
||||||
placeholder="e.g. about me"
|
placeholder={formatMessage({ defaultMessage: "e.g. about me" })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-control">
|
<div className="form-control">
|
||||||
@ -220,7 +221,10 @@ function CardDialog({ header, cta, cancelCta, card, onSave, onCancel }: CardDial
|
|||||||
<label htmlFor="card-content">
|
<label htmlFor="card-content">
|
||||||
<FormattedMessage defaultMessage="Content" />
|
<FormattedMessage defaultMessage="Content" />
|
||||||
</label>
|
</label>
|
||||||
<textarea placeholder="Start typing..." value={content} onChange={e => setContent(e.target.value)} />
|
<textarea
|
||||||
|
placeholder={formatMessage({ defaultMessage: "Start typing" })}
|
||||||
|
value={content}
|
||||||
|
onChange={e => setContent(e.target.value)} />
|
||||||
<span className="help-text">
|
<span className="help-text">
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Supports {markdown}"
|
defaultMessage="Supports {markdown}"
|
||||||
|
23
src/intl.tsx
23
src/intl.tsx
@ -5,18 +5,34 @@ import enMessages from "translations/en.json";
|
|||||||
|
|
||||||
const DefaultLocale = "en-US";
|
const DefaultLocale = "en-US";
|
||||||
|
|
||||||
|
async function importLang(code: string) {
|
||||||
|
const src = await import(`translations/${code}.json`);
|
||||||
|
const typed = src.default as Record<string, { defaultMessage: string }>;
|
||||||
|
const ent = Object.entries(typed).map(([k, v]) => [k, v.defaultMessage]);
|
||||||
|
return Object.fromEntries(ent) as Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
const getMessages = (locale: string) => {
|
const getMessages = (locale: string) => {
|
||||||
const truncatedLocale = locale.toLowerCase().split(/[_-]+/)[0];
|
const truncatedLocale = locale.toLowerCase().split(/[_-]+/)[0];
|
||||||
|
|
||||||
const matchLang = (lng: string) => {
|
const matchLang = async (lng: string) => {
|
||||||
switch (lng) {
|
switch (lng) {
|
||||||
|
case "de":
|
||||||
|
case "de-DE":
|
||||||
|
return await importLang("de_DE");
|
||||||
|
case "es":
|
||||||
|
case "es-ES":
|
||||||
|
return await importLang("es_ES");
|
||||||
|
case "th":
|
||||||
|
case "th-TH":
|
||||||
|
return await importLang("th_TH");
|
||||||
case DefaultLocale:
|
case DefaultLocale:
|
||||||
case "en":
|
case "en":
|
||||||
return enMessages;
|
return enMessages;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return matchLang(locale) ?? matchLang(truncatedLocale) ?? enMessages;
|
return matchLang(locale) ?? matchLang(truncatedLocale) ?? Promise.resolve(enMessages);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IntlProvider = ({ children }: { children: ReactNode }) => {
|
export const IntlProvider = ({ children }: { children: ReactNode }) => {
|
||||||
@ -24,8 +40,7 @@ export const IntlProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
const [messages, setMessages] = useState<Record<string, string>>(enMessages);
|
const [messages, setMessages] = useState<Record<string, string>>(enMessages);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const msg = getMessages(locale);
|
getMessages(locale).then(a => setMessages(a as Record<string, string>));
|
||||||
setMessages(msg);
|
|
||||||
}, [locale]);
|
}, [locale]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -83,10 +83,10 @@ export function LayoutPage() {
|
|||||||
</Helmet>
|
</Helmet>
|
||||||
<header>
|
<header>
|
||||||
<div className="logo" onClick={() => navigate("/")}></div>
|
<div className="logo" onClick={() => navigate("/")}></div>
|
||||||
<div className="paper">
|
{/*<div className="paper">
|
||||||
<input className="search-input" type="text" placeholder="Search" />
|
<input className="search-input" type="text" placeholder="Search" />
|
||||||
<Icon name="search" size={15} />
|
<Icon name="search" size={15} />
|
||||||
</div>
|
</div>*/}
|
||||||
<div className="f-grow">{/* Future menu items go here */}</div>
|
<div className="f-grow">{/* Future menu items go here */}</div>
|
||||||
<div className="header-right">
|
<div className="header-right">
|
||||||
{loggedIn()}
|
{loggedIn()}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import "./root.css";
|
import "./root.css";
|
||||||
|
import { FormattedMessage } from "react-intl";
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import type { NostrEvent } from "@snort/system";
|
import type { NostrEvent } from "@snort/system";
|
||||||
|
|
||||||
@ -31,7 +32,9 @@ export function RootPage() {
|
|||||||
<div className="homepage">
|
<div className="homepage">
|
||||||
{hasFollowingLive && (
|
{hasFollowingLive && (
|
||||||
<>
|
<>
|
||||||
<h2 className="divider line one-line">Following</h2>
|
<h2 className="divider line one-line">
|
||||||
|
<FormattedMessage defaultMessage="Following" />
|
||||||
|
</h2>
|
||||||
<div className="video-grid">
|
<div className="video-grid">
|
||||||
{following.map(e => (
|
{following.map(e => (
|
||||||
<VideoTile ev={e} key={e.id} />
|
<VideoTile ev={e} key={e.id} />
|
||||||
@ -66,7 +69,9 @@ export function RootPage() {
|
|||||||
))}
|
))}
|
||||||
{hasFollowingLive && liveNow.length > 0 && (
|
{hasFollowingLive && liveNow.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<h2 className="divider line one-line">Live</h2>
|
<h2 className="divider line one-line">
|
||||||
|
<FormattedMessage defaultMessage="Live" />
|
||||||
|
</h2>
|
||||||
<div className="video-grid">
|
<div className="video-grid">
|
||||||
{liveNow
|
{liveNow
|
||||||
.filter(e => !mutedHosts.has(getHost(e)))
|
.filter(e => !mutedHosts.has(getHost(e)))
|
||||||
@ -78,7 +83,9 @@ export function RootPage() {
|
|||||||
)}
|
)}
|
||||||
{plannedEvents.length > 0 && (
|
{plannedEvents.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<h2 className="divider line one-line">Planned</h2>
|
<h2 className="divider line one-line">
|
||||||
|
<FormattedMessage defaultMessage="Planned" />
|
||||||
|
</h2>
|
||||||
<div className="video-grid">
|
<div className="video-grid">
|
||||||
{plannedEvents.map(e => (
|
{plannedEvents.map(e => (
|
||||||
<VideoTile ev={e} key={e.id} />
|
<VideoTile ev={e} key={e.id} />
|
||||||
@ -88,7 +95,9 @@ export function RootPage() {
|
|||||||
)}
|
)}
|
||||||
{endedEvents.length > 0 && (
|
{endedEvents.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<h2 className="divider line one-line">Ended</h2>
|
<h2 className="divider line one-line">
|
||||||
|
<FormattedMessage defaultMessage="Ended" />
|
||||||
|
</h2>
|
||||||
<div className="video-grid">
|
<div className="video-grid">
|
||||||
{endedEvents.map(e => (
|
{endedEvents.map(e => (
|
||||||
<VideoTile ev={e} key={e.id} />
|
<VideoTile ev={e} key={e.id} />
|
||||||
|
Loading…
Reference in New Issue
Block a user