feat: language switch (#903)
This commit is contained in:
parent
f822b29915
commit
64909965e1
|
|
@ -1,5 +1,6 @@
|
|||
import { ConnectButton } from "~/components/common/ConnectButton"
|
||||
import { DarkModeSwitch } from "~/components/common/DarkModeSwitch"
|
||||
import { LanguageSwitch } from "~/components/common/LanguageSwitch"
|
||||
import { Logo } from "~/components/common/Logo"
|
||||
import HomeTabs from "~/components/home/HomeTabs"
|
||||
import PromotionLinks from "~/components/home/PromotionLinks"
|
||||
|
|
@ -45,6 +46,7 @@ export default async function HomeLayout({
|
|||
<PromotionLinks className="w-full sm:w-72" />
|
||||
<span className="inline-flex items-center space-y-4 sm:space-y-0 sm:space-x-4 mx-auto sm:mx-0 mt-10 sm:mt-0 flex-col sm:flex-row">
|
||||
<DarkModeSwitch />
|
||||
<LanguageSwitch />
|
||||
<span>
|
||||
©{" "}
|
||||
<UniLink
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
"use client"
|
||||
|
||||
import { changeLanguage, useAvailableLanguages } from "~/lib/i18n/client"
|
||||
|
||||
import { Menu } from "../ui/Menu"
|
||||
|
||||
export function LanguageSwitch() {
|
||||
const languages = useAvailableLanguages()
|
||||
|
||||
return (
|
||||
<Menu
|
||||
placement="top"
|
||||
target={
|
||||
<button className="inline-block icon-[mingcute--translate-2-line] text-2xl"></button>
|
||||
}
|
||||
dropdown={
|
||||
<>
|
||||
{languages.map((language, i) => (
|
||||
<Menu.Item
|
||||
key={i}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
changeLanguage(language.code)
|
||||
}}
|
||||
className="mx-auto"
|
||||
>
|
||||
<span>{language.name}</span>
|
||||
{language.active && (
|
||||
<span className="ml-2 icon-[mingcute--check-line]"></span>
|
||||
)}
|
||||
</Menu.Item>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import { Trans, getTranslation } from "~/lib/i18n"
|
|||
import { ExpandedCharacter } from "~/lib/types"
|
||||
|
||||
import { DarkModeSwitch } from "../common/DarkModeSwitch"
|
||||
import { LanguageSwitch } from "../common/LanguageSwitch"
|
||||
import { UniLink } from "../ui/UniLink"
|
||||
import ConnectedAccounts from "./ConnectedAccounts"
|
||||
|
||||
|
|
@ -52,7 +53,10 @@ export default async function SiteFooter({
|
|||
<ConnectedAccounts
|
||||
connectedAccounts={site?.metadata?.content?.connected_accounts}
|
||||
/>
|
||||
<DarkModeSwitch />
|
||||
<div className="flex gap-x-2 items-center">
|
||||
<LanguageSwitch />
|
||||
<DarkModeSwitch />
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
{site?.metadata?.content?.ga && (
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import Link from "next/link"
|
||||
import React, { Fragment } from "react"
|
||||
|
||||
import { autoUpdate, shift, useFloating } from "@floating-ui/react"
|
||||
import { Placement, autoUpdate, shift, useFloating } from "@floating-ui/react"
|
||||
import { Menu as HeadlessUiMenu } from "@headlessui/react"
|
||||
|
||||
import { cn } from "~/lib/utils"
|
||||
|
|
@ -19,7 +19,7 @@ export function Menu({
|
|||
}: React.PropsWithChildren<{
|
||||
target: JSX.Element
|
||||
dropdown: React.ReactNode
|
||||
placement?: "bottom-start" | "bottom-end"
|
||||
placement?: Placement
|
||||
}>) {
|
||||
const { refs, floatingStyles } = useFloating({
|
||||
placement,
|
||||
|
|
|
|||
|
|
@ -1,19 +1,25 @@
|
|||
import { headers } from "next/headers"
|
||||
import { cookies, headers } from "next/headers"
|
||||
|
||||
import { fallbackLng } from "~/lib/i18n/settings"
|
||||
|
||||
export function getAcceptLang() {
|
||||
let lng = fallbackLng
|
||||
|
||||
const preferLang = cookies().get("preferred_language")?.value
|
||||
|
||||
let acceptLang = headers().get("accept-language")?.split(",")[0]
|
||||
if (acceptLang === "zh-HK" || acceptLang === "zh-TW") {
|
||||
acceptLang = "zh-TW"
|
||||
} else {
|
||||
acceptLang = acceptLang?.split("-")[0]
|
||||
}
|
||||
|
||||
if (acceptLang) {
|
||||
lng = acceptLang
|
||||
}
|
||||
if (preferLang) {
|
||||
lng = preferLang
|
||||
}
|
||||
|
||||
return lng
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import i18next from "i18next"
|
||||
import resourcesToBackend from "i18next-resources-to-backend"
|
||||
import { useEffect, useState } from "react"
|
||||
import {
|
||||
initReactI18next,
|
||||
useTranslation as useTranslationOrg,
|
||||
|
|
@ -10,7 +11,7 @@ import { Trans as TransW } from "react-i18next/TransWithoutContext"
|
|||
|
||||
import { useLang } from "~/hooks/useLang"
|
||||
|
||||
import { defaultNS, getOptions, languages } from "./settings"
|
||||
import { defaultNS, getOptions, languageNames, languages } from "./settings"
|
||||
|
||||
i18next
|
||||
.use(initReactI18next)
|
||||
|
|
@ -33,3 +34,46 @@ export function useTranslation(ns: string = defaultNS) {
|
|||
}
|
||||
|
||||
export const Trans = TransW
|
||||
|
||||
export function isLanguageAuto() {
|
||||
return !window.document.cookie.includes("preferred_language")
|
||||
}
|
||||
|
||||
export function changeLanguage(language: string) {
|
||||
if (language === "auto") {
|
||||
document.cookie =
|
||||
"preferred_language=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"
|
||||
} else {
|
||||
document.cookie = `preferred_language=${language};`
|
||||
}
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
export function useAvailableLanguages() {
|
||||
const supportedLngs = i18next.options.supportedLngs
|
||||
const languages =
|
||||
typeof supportedLngs === "object"
|
||||
? supportedLngs.filter((i) => i !== "cimode")
|
||||
: []
|
||||
const [isMounted, setIsMounted] = useState(false)
|
||||
|
||||
const [currentLng, setCurrentLng] = useState(i18next.language)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isMounted) {
|
||||
setIsMounted(true)
|
||||
return
|
||||
}
|
||||
if (isMounted && isLanguageAuto()) {
|
||||
setCurrentLng("auto")
|
||||
}
|
||||
}, [isMounted])
|
||||
|
||||
return languages.concat("auto").map((lang) => {
|
||||
return {
|
||||
code: lang,
|
||||
name: languageNames[lang as keyof typeof languageNames],
|
||||
active: lang === currentLng,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
export const fallbackLng = "en"
|
||||
export const languages = ["en", "zh", "zh-TW", "ja"]
|
||||
export const languageNames = {
|
||||
en: "English",
|
||||
zh: "中文",
|
||||
"zh-TW": "中文(繁體)",
|
||||
ja: "日本語",
|
||||
auto: "Auto",
|
||||
}
|
||||
export const defaultNS = "common"
|
||||
|
||||
export function getOptions(lng = fallbackLng, ns = defaultNS) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue