feat: add typescript support to i18next

This commit is contained in:
Tristan Yang
2022-11-25 11:28:00 +08:00
parent 79832fefe3
commit 48a529e5d6
25 changed files with 136 additions and 54 deletions
+4 -3
View File
@@ -3,10 +3,11 @@ import "dayjs/locale/zh-cn";
import relativeTime from "dayjs/plugin/relativeTime";
import isToday from "dayjs/plugin/isToday";
import isYesterday from "dayjs/plugin/isYesterday";
import i18n from '../i18n';
// import i18n from '../i18n';
dayjs.extend(relativeTime);
dayjs.extend(isToday);
dayjs.extend(isYesterday);
dayjs.locale(i18n.language == "en" ? "en" : "zh-cn");
// console.log("dddd", i18n.resolvedLanguage);
// if(i18n.language)
// dayjs.locale(i18n.language == "en" ? "en" : "zh-cn");
+1 -1
View File
@@ -100,7 +100,7 @@ const ChannelModal: FC<Props> = ({ personal = false, closeModal }) => {
<input
value={input}
onChange={handleInputChange}
placeholder={t("search_user_placeholder") || ""}
placeholder={t("search_user_placeholder")}
/>
</div>
{users && (
@@ -97,7 +97,7 @@ const InviteByEmail: FC<Props> = ({ cid }) => {
onChange={handleEmail}
disabled={!enableSMTP}
type="email"
placeholder={enableSMTP ? "Enter Email" : t("enable_smtp") || ""}
placeholder={enableSMTP ? "Enter Email" : t("enable_smtp")}
/>
<Button disabled={!enableSMTP || !email} className="send">
{t("action.send", { ns: "common" })}
@@ -30,11 +30,11 @@ const LeaveConfirmModal: FC<Props> = ({ id, closeModal, handleNextStep }) => {
<Modal id="modal-modal">
<StyledModal
className="compact"
title={t("channel.leave") || ""}
title={t("channel.leave")}
description={
isOwner
? t("channel.transfer_desc") || ""
: t("channel.leave_desc") || ""
? t("channel.transfer_desc")
: t("channel.leave_desc")
}
buttons={
<>
@@ -60,7 +60,7 @@ const PinMessageModal: FC<Props> = ({ closeModal, mid = 0, gid = 0 }) => {
</Button>
</>
}
title={t("action.pin") || ""}
title={t("action.pin")}
description={`Do you want to pin this message to #${channel?.name}`}
>
<PreviewMessage mid={mid} />
+1 -1
View File
@@ -70,7 +70,7 @@ const UsersModal: FC<Props> = ({ closeModal }) => {
<Modal>
<StyledWrapper ref={wrapperRef}>
<div className="search">
<input value={input} onChange={handleSearch} placeholder={t("search_user_placeholder") || ""} />
<input value={input} onChange={handleSearch} placeholder={t("search_user_placeholder")} />
</div>
{users && (
<ul className="users">
+39 -6
View File
@@ -1,12 +1,39 @@
import i18n from 'i18next';
import dayjs from 'dayjs';
import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import common from '../public/locales/en/common.json';
import auth from '../public/locales/en/auth.json';
import member from '../public/locales/en/member.json';
import chat from '../public/locales/en/chat.json';
import fav from '../public/locales/en/fav.json';
import welcome from '../public/locales/en/welcome.json';
import setting from '../public/locales/en/setting.json';
import file from '../public/locales/en/file.json';
// don't want to use this?
// have a look at the Quick start guide
// for passing in lng and translations on init
export const defaultNS = "common";
export const resources = {
en: {
common,
chat,
auth,
fav,
member,
welcome,
setting,
file,
},
} as const;
i18n.on("languageChanged", (lng) => {
console.log("changed", lng);
dayjs.locale(lng === 'zh' ? "zh-cn" : lng);
});
i18n
// load translation using http -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales)
// learn more: https://github.com/i18next/i18next-http-backend
@@ -21,12 +48,18 @@ i18n
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
ns: ["common", "chat", "member", "setting", "fav", "file", "welcome", "auth"],
defaultNS: "common",
defaultNS,
// lng: "en",
fallbackLng: 'en',
debug: true,
// interpolation: {
// escapeValue: false, // not needed for react as it escapes by default
// }
fallbackNS: "common",
debug: false,
detection: {
order: ["localStorage", "navigator"]
},
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
returnNull: false,
});
+10
View File
@@ -0,0 +1,10 @@
import { resources, defaultNS } from "./i18n";
declare module "i18next" {
interface CustomTypeOptions {
allowObjectInHTMLChildren: true,
returnNull: false;
defaultNS: typeof defaultNS;
resources: typeof resources["en"];
}
}
+2 -2
View File
@@ -139,7 +139,7 @@ export default function LoginPage() {
name="email"
value={email}
required
placeholder={t("placeholder_email") || ""}
placeholder={t("placeholder_email")}
data-type="email"
onChange={handleInput}
/>
@@ -151,7 +151,7 @@ export default function LoginPage() {
required
data-type="password"
onChange={handleInput}
placeholder={t("placeholder_pwd") || ""}
placeholder={t("placeholder_pwd")}
/>
<Button type="submit" disabled={isLoading}>
{isLoading ? "Signing" : t("sign_in")}
+3 -3
View File
@@ -119,7 +119,7 @@ export default function Reg() {
name="email"
value={email}
required
placeholder={t("placeholder_email") || ""}
placeholder={t("placeholder_email")}
data-type="email"
onChange={handleInput}
/>
@@ -131,7 +131,7 @@ export default function Reg() {
required
data-type="password"
onChange={handleInput}
placeholder={t("placeholder_pwd") || ""}
placeholder={t("placeholder_pwd")}
/>
<Input
required
@@ -141,7 +141,7 @@ export default function Reg() {
value={confirmPassword}
data-type="confirmPassword"
onChange={handleInput}
placeholder={t("placeholder_confirm_pwd") || ""}
placeholder={t("placeholder_confirm_pwd")}
></Input>
<Button type="submit" disabled={isLoading}>
{isLoading ? "Signing Up" : t("sign_up")}
+5 -3
View File
@@ -1,5 +1,6 @@
import Button from "../../common/component/styled/Button";
import { FC, MouseEvent } from "react";
import { useTranslation } from "react-i18next";
interface Props {
email: string;
@@ -7,14 +8,15 @@ interface Props {
}
const SentTip: FC<Props> = ({ email, reset }) => {
const { t } = useTranslation("auth");
return (
<div className="tips">
<h2 className="title">Check your email</h2>
<h2 className="title">{t("check_email")}</h2>
<span className="desc">
Weve sent you a magic link to {email}. Click on the link to continue.
{t("check_email_desc", { email })}
</span>
<Button onClick={reset} className="main flex">
Use a different email
{t("use_different")}
</Button>
</div>
);
+7 -5
View File
@@ -7,8 +7,10 @@ import Input from "../../common/component/styled/Input";
import Button from "../../common/component/styled/Button";
import { useSendLoginMagicLinkMutation } from "../../app/services/auth";
import SentTip from "./SentTip";
import { useTranslation } from "react-i18next";
export default function SendMagicLinkPage() {
const { t } = useTranslation("auth");
const [sent, setSent] = useState(false);
const [sendMagicLink, { isSuccess, isLoading, error }] = useSendLoginMagicLinkMutation();
const navigateTo = useNavigate();
@@ -69,8 +71,8 @@ export default function SendMagicLinkPage() {
<>
<div className="tips">
<img src={`${BASE_URL}/resource/organization/logo`} alt="logo" className="logo" />
<h2 className="title">Login to VoceChat</h2>
<span className="desc">Please enter your Email</span>
<h2 className="title">{t("login.title")}</h2>
<span className="desc">{t("placeholder_email")}</span>
</div>
<form onSubmit={handleLogin}>
<Input
@@ -81,15 +83,15 @@ export default function SendMagicLinkPage() {
value={email}
required
// pattern={`^\S+@\S+\.\S+$`}
placeholder="Enter your email"
placeholder={t("placeholder_email")}
onChange={handleInput}
/>
<Button type="submit" disabled={isLoading || !email}>
{isLoading ? "Sending" : `Continue with Email`}
{isLoading ? "Sending" : t("continue")}
</Button>
</form>
<hr className="or" />
<Button onClick={handlePwdPath}>Sign in with Password</Button>
<Button onClick={handlePwdPath}>{t("login.password")}</Button>
</>
)}
</div>
+4 -3
View File
@@ -62,7 +62,8 @@ const Styled = styled.div`
`;
export default function APIConfig() {
const { t } = useTranslation(["setting", "common"]);
const { t } = useTranslation("setting");
const { t: ct } = useTranslation();
const { updateConfig, values } = useConfig("login");
const { data } = useGetThirdPartySecretQuery();
const [updateSecret, { data: updatedSecret, isSuccess, isLoading }] =
@@ -99,10 +100,10 @@ export default function APIConfig() {
</div>
<div className="btns">
<Button onClick={() => hideAll()} className="cancel small">
{t("action.cancel", { ns: "common" })}
{ct("action.cancel")}
</Button>
<Button disabled={isLoading} className="small danger" onClick={() => updateSecret()}>
{t("action.yes", { ns: "common" })}
{ct("action.yes")}
</Button>
</div>
</StyledConfirm>
@@ -48,8 +48,8 @@ const LicensePriceListModal: FC<Props> = ({ closeModal }) => {
return (
<Modal id="modal-modal">
<StyledModal
title={t("license.renew") || ""}
description={t("license.renew_select") || ""}
title={t("license.renew")}
description={t("license.renew_select")}
buttons={
<>
<Button onClick={closeModal} className="ghost">
-2
View File
@@ -1,5 +1,4 @@
// import React from 'react'
import dayjs from 'dayjs';
import { useTranslation } from 'react-i18next';
import StyledRadio from "../../../common/component/styled/Radio";
@@ -9,7 +8,6 @@ const Index = () => {
const { t, i18n } = useTranslation("setting");
const handleGuestToggle = (v: "zh" | "en") => {
i18n.changeLanguage(v);
dayjs.locale(v === "zh" ? "zh-cn" : "en");
};
return (
<div className="setting">
@@ -24,8 +24,8 @@ const RemoveConfirmModal: FC<Props> = ({ closeModal }) => {
return (
<Modal id="modal-modal">
<StyledModal
title={t("remove_account") || ""}
description={t("remove_account_desc") || ""}
title={t("remove_account")}
description={t("remove_account_desc")}
buttons={
<>
<Button onClick={closeModal}>{t("action.cancel", { ns: "common" })}</Button>
+2 -2
View File
@@ -77,8 +77,8 @@ const ProfileBasicEditModal: FC<Props> = ({ closeModal }) => {
return (
<Modal id="modal-modal">
<StyledEdit
title={t("change_pwd") || ""}
description={t("change_pwd_desc") || ""}
title={t("change_pwd")}
description={t("change_pwd_desc")}
buttons={
<>
<Button className="cancel" onClick={closeModal}>
+4 -4
View File
@@ -47,7 +47,7 @@ export default function ConfigFirebase() {
onChange={handleChange}
value={token_url}
name="token_url"
placeholder={t("firebase.token_url") || ""}
placeholder={t("firebase.token_url")}
/>
</div>
<div className="input">
@@ -58,7 +58,7 @@ export default function ConfigFirebase() {
onChange={handleChange}
value={project_id}
name="project_id"
placeholder={t("firebase.project_id") || ""}
placeholder={t("firebase.project_id")}
/>
</div>
<div className="input">
@@ -71,7 +71,7 @@ export default function ConfigFirebase() {
onChange={handleChange}
value={private_key}
name="private_key"
placeholder={t("firebase.private_key") || ""}
placeholder={t("firebase.private_key")}
/>
</div>
<div className="input">
@@ -82,7 +82,7 @@ export default function ConfigFirebase() {
onChange={handleChange}
value={client_email}
name="client_email"
placeholder={t("firebase.client_email") || ""}
placeholder={t("firebase.client_email")}
/>
</div>
</div>
+1 -1
View File
@@ -32,7 +32,7 @@ export default function Setting() {
<StyledSettingContainer
nav={currNav}
closeModal={close}
title={t("setting") || ""}
title={t("setting")}
navs={navs}
dangers={[{ title: t("action.logout"), handler: toggleLogoutConfirm }]}
>
@@ -33,8 +33,8 @@ const DeleteConfirmModal: FC<Props> = ({ id, closeModal }) => {
<Modal id="modal-modal">
<StyledModal
className="compact"
title={t("channel.delete") || ""}
description={t("channel.delete_desc") || ""}
title={t("channel.delete")}
description={t("channel.delete_desc")}
buttons={
<>
<Button onClick={closeModal.bind(null, undefined)} className="cancel">
+2 -2
View File
@@ -133,7 +133,7 @@ export default function Overview({ id = 0 }) {
value={name}
name="name"
id="name"
placeholder={t("name") || ""}
placeholder={t("name")}
/>
</div>
<div className="input">
@@ -146,7 +146,7 @@ export default function Overview({ id = 0 }) {
rows={4}
name="name"
id="name"
placeholder={t("topic_placeholder") || ""} />
placeholder={t("topic_placeholder")} />
</div>
{!readOnly && <div className="input">
<Label htmlFor="desc">{t("visibility")}</Label>