feat:change language setup

This commit is contained in:
Rama Priyanto 2024-12-11 17:13:02 +07:00
parent 400a01d0bb
commit 89c71a5acc
9 changed files with 2131 additions and 1317 deletions

View File

@ -1,3 +1,5 @@
// RootLayout.tsx
"use client";
import { fontSans } from "@/config/fonts";
import { siteConfig } from "@/config/site";
import "@/styles/globals.css";
@ -5,29 +7,55 @@ import clsx from "clsx";
import { Metadata } from "next";
import { Providers } from "./providers";
import LoadScript from "@/utils/global";
import { NextIntlClientProvider } from "next-intl";
import { useEffect, useState, type ReactNode } from "react";
import storedLanguage from "@/store/language-store";
export const metadata: Metadata = {
title: {
default: siteConfig.name,
template: `%s - ${siteConfig.name}`,
},
description: siteConfig.description,
themeColor: [
{ media: "(prefers-color-scheme: light)", color: "white" },
{ media: "(prefers-color-scheme: dark)", color: "black" },
],
icons: {
icon: "/logohumas.ico",
shortcut: "/favicon-16x16.png",
apple: "/apple-touch-icon.png",
},
};
// export const metadata: Metadata = {
// title: {
// default: siteConfig.name,
// template: `%s - ${siteConfig.name}`,
// },
// description: siteConfig.description,
// themeColor: [
// { media: "(prefers-color-scheme: light)", color: "white" },
// { media: "(prefers-color-scheme: dark)", color: "black" },
// ],
// icons: {
// icon: "/logohumas.ico",
// shortcut: "/favicon-16x16.png",
// apple: "/apple-touch-icon.png",
// },
// };
export default function RootLayout({
children,
params,
}: {
children: React.ReactNode;
children: ReactNode;
params: { locale: string };
}) {
// const { locale } = params;
const [localeNow, setLocaleNow] = useState<string>("id");
const locale = storedLanguage((state) => state.locale);
// Load messages dynamically based on locale
// const messages = (await import(`../messages/${locale}.json`)).default;
useEffect(() => {
if (locale) {
setLocaleNow(locale);
}
}, [locale]);
const [messages, setMessages] = useState<any>(null);
useEffect(() => {
(async () => {
const loadedMessages = (await import(`../messages/${locale}.json`))
.default;
setMessages(loadedMessages);
})();
}, [locale]);
return (
<html lang="en" suppressHydrationWarning className="scroll-smooth">
<head>
@ -49,9 +77,11 @@ export default function RootLayout({
fontSans.variable
)}
>
<NextIntlClientProvider locale={localeNow} messages={messages}>
<Providers themeProps={{ attribute: "class", defaultTheme: "dark" }}>
<main className="">{children}</main>
</Providers>
</NextIntlClientProvider>
</body>
</html>
);

File diff suppressed because one or more lines are too long

View File

@ -29,11 +29,14 @@ import {
SearchIcon,
TtIcon,
TwIcon,
UKIcon,
YtIcon,
} from "../../icons";
import { ThemeSwitch } from "../../theme-switch";
import Cookies from "js-cookie";
import { useRouter } from "next/navigation";
import { useTranslations } from "next-intl";
import storedLanguage from "@/store/language-store";
interface MenuItem {
key: string;
@ -54,15 +57,18 @@ interface DropdownOpenState {
export default function NavbarHumas() {
const [dropdownOpen, setDropdownOpen] = useState<DropdownOpenState>({});
const router = useRouter();
const t = useTranslations("Navbar");
const token = Cookies.get("access_token");
const isAuthenticated = Cookies.get("is_authenticated");
// useEffect(() => {
// if (!isAuthenticated) {
// onLogout();
// }
// }, [token]);
const language = storedLanguage((state) => state.locale);
const setLanguage = storedLanguage((state) => state.setLocale);
useEffect(() => {
if (!isAuthenticated) {
onLogout();
}
}, [token]);
const onLogout = () => {
Object.keys(Cookies.get()).forEach((cookieName) => {
@ -143,13 +149,18 @@ export default function NavbarHumas() {
<TtIcon />
</Link>
</div>
<div>
<IdnIcon />
</div>
<a
className="cursor-pointer"
onClick={() =>
language === "id" ? setLanguage("en") : setLanguage("id")
}
>
{language === "id" ? <IdnIcon /> : <UKIcon />}
</a>
</div>
<div className="flex flex-wrap items-center justify-around ">
<Link href={"/"}>
<div>Beranda</div>
<div>{t("beranda")}</div>
</Link>
<div>
<Dropdown className=" dark:bg-[#1F1A17]">
@ -162,13 +173,13 @@ export default function NavbarHumas() {
variant="light"
endContent={<ChevronDownIcon className="pt-1" />}
>
Tentang
{t("tentang")}
</Button>
</DropdownTrigger>
</NavbarItem>
<DropdownMenu
// aria-label="tentang"
title="Tentang"
title={t("tentang")}
className="pt-4"
classNames={{
// base: "flex",
@ -185,7 +196,7 @@ export default function NavbarHumas() {
href="/static/humas-polri"
className="flex justify-between"
>
Tentang Humas POLRI
{t("tentang")} Humas POLRI
<ChevronRightIcon />
</Link>
</DropdownItem>
@ -194,7 +205,7 @@ export default function NavbarHumas() {
href="/static/profile-kapolri"
className="flex justify-between"
>
Profile Pimpinan POLRI
{t("profilPimpinan")}
<ChevronRightIcon />
</Link>
</DropdownItem>
@ -203,7 +214,7 @@ export default function NavbarHumas() {
href="/static/struktur-mabes"
className="flex justify-between"
>
Struktur Organisasi
{t("strukturOrganisasi")}
<ChevronRightIcon />
</Link>
</DropdownItem>
@ -212,7 +223,7 @@ export default function NavbarHumas() {
href="/static/visi-misi-polri"
className="flex justify-between"
>
Visi & Misi
{t("visi")} & {t("misi")}
<ChevronRightIcon />
</Link>
</DropdownItem>
@ -221,7 +232,7 @@ export default function NavbarHumas() {
href="/static/tugas-dan-fungsi-polri"
className="flex justify-between"
>
Tugas & Fungsi
{t("tugas")} & {t("fungsi")}
<ChevronRightIcon />
</Link>
</DropdownItem>
@ -235,7 +246,10 @@ export default function NavbarHumas() {
</Dropdown>
</div>
<div>
<Link href="http://103.82.242.92:5555/" target="_blank">
{/* <Link href="http://103.82.242.92:5555/" target="_blank">
Portal PPID
</Link> */}
<Link href="http://siberpresisi.com/" target="_blank">
Portal PPID
</Link>
</div>
@ -250,7 +264,7 @@ export default function NavbarHumas() {
variant="light"
endContent={<ChevronDownIcon className="pt-1" />}
>
Pelayanan Masyarakat
{t("pelayananMasyarakat")}
</Button>
</DropdownTrigger>
</NavbarItem>
@ -378,7 +392,7 @@ export default function NavbarHumas() {
</Dropdown>
</div>
<div>
<Link href={"/kontak-kami"}>Kontak</Link>
<Link href={"/kontak-kami"}>{t("kontak")}</Link>
</div>
<div className="flex items-center gap-3">
<div className="hidden lg:block">{searchInput}</div>
@ -414,7 +428,7 @@ export default function NavbarHumas() {
</Dropdown>
) : (
<Link href="/auth">
<Button className="bg-[#DD8306]">Login</Button>
<Button className="bg-[#DD8306] text-white">Login</Button>
</Link>
)}
</div>
@ -431,12 +445,17 @@ export default function NavbarHumas() {
<NavbarMenuItem>
{item.key === "login" ? (
token ? (
<Button className="bg-[#DD8306]" onPress={onLogout}>
<Button
className="bg-[#DD8306] text-white"
onPress={onLogout}
>
Logout
</Button>
) : (
<Link href="/auth">
<Button className="bg-[#DD8306]">Login</Button>
<Button className="bg-[#DD8306] text-white">
Login
</Button>
</Link>
)
) : item.key === "dashboard" ? (

15
i18n/request.ts Normal file
View File

@ -0,0 +1,15 @@
"use client";
import { notFound } from "next/navigation";
import { getRequestConfig } from "next-intl/server";
import storedLanguage from "@/store/language-store";
export default getRequestConfig(async () => {
const supportedLocale = ["en", "id"];
const locale = storedLanguage((state) => state.locale);
// Validate that the incoming `locale` parameter is valid
if (!supportedLocale.includes(locale)) notFound();
return {
messages: (await import(`../messages/${locale}.json`)).default,
};
});

14
messages/en.json Normal file
View File

@ -0,0 +1,14 @@
{
"Navbar": {
"beranda": "Home",
"tentang": "About",
"pelayananMasyarakat": "Public Service",
"kontak": "Contact",
"visi": "Vision",
"misi": "Mission",
"strukturOrganisasi": "Organizational Structure",
"profilPimpinan": "Profile of The Police Leader",
"tugas": "Duties",
"fungsi": "Functions"
}
}

14
messages/id.json Normal file
View File

@ -0,0 +1,14 @@
{
"Navbar": {
"beranda": "Beranda",
"tentang": "Tentang",
"pelayananMasyarakat": "Pelayanan Masyarakat",
"kontak": "Kontak",
"visi": "Visi",
"misi": "Misi",
"strukturOrganisasi": "Struktur Organisasi",
"profilPimpinan": "Profil Pimpinan Polri",
"tugas": "Tugas",
"fungsi": "Fungsi"
}
}

104
package-lock.json generated
View File

@ -42,6 +42,7 @@
"jodit-react": "^4.0.25",
"js-cookie": "^3.0.5",
"next": "14.0.2",
"next-intl": "^3.26.0",
"next-themes": "^0.2.1",
"postcss": "8.4.31",
"react": "18.2.0",
@ -434,42 +435,49 @@
"integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q=="
},
"node_modules/@formatjs/ecma402-abstract": {
"version": "1.17.3",
"license": "MIT",
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.1.tgz",
"integrity": "sha512-Ip9uV+/MpLXWRk03U/GzeJMuPeOXpJBSB5V1tjA6kJhvqssye5J5LoYLc7Z5IAHb7nR62sRoguzrFiVCP/hnzw==",
"dependencies": {
"@formatjs/intl-localematcher": "0.5.0",
"tslib": "^2.4.0"
"@formatjs/fast-memoize": "2.2.5",
"@formatjs/intl-localematcher": "0.5.9",
"decimal.js": "10",
"tslib": "2"
}
},
"node_modules/@formatjs/fast-memoize": {
"version": "2.2.0",
"license": "MIT",
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.5.tgz",
"integrity": "sha512-6PoewUMrrcqxSoBXAOJDiW1m+AmkrAj0RiXnOMD59GRaswjXhm3MDhgepXPBgonc09oSirAJTsAggzAGQf6A6g==",
"dependencies": {
"tslib": "^2.4.0"
"tslib": "2"
}
},
"node_modules/@formatjs/icu-messageformat-parser": {
"version": "2.7.1",
"license": "MIT",
"version": "2.9.7",
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.9.7.tgz",
"integrity": "sha512-cuEHyRM5VqLQobANOjtjlgU7+qmk9Q3fDQuBiRRJ3+Wp3ZoZhpUPtUfuimZXsir6SaI2TaAJ+SLo9vLnV5QcbA==",
"dependencies": {
"@formatjs/ecma402-abstract": "1.17.3",
"@formatjs/icu-skeleton-parser": "1.6.3",
"tslib": "^2.4.0"
"@formatjs/ecma402-abstract": "2.3.1",
"@formatjs/icu-skeleton-parser": "1.8.11",
"tslib": "2"
}
},
"node_modules/@formatjs/icu-skeleton-parser": {
"version": "1.6.3",
"license": "MIT",
"version": "1.8.11",
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.11.tgz",
"integrity": "sha512-8LlHHE/yL/zVJZHAX3pbKaCjZKmBIO6aJY1mkVh4RMSEu/2WRZ4Ysvv3kKXJ9M8RJLBHdnk1/dUQFdod1Dt7Dw==",
"dependencies": {
"@formatjs/ecma402-abstract": "1.17.3",
"tslib": "^2.4.0"
"@formatjs/ecma402-abstract": "2.3.1",
"tslib": "2"
}
},
"node_modules/@formatjs/intl-localematcher": {
"version": "0.5.0",
"license": "MIT",
"version": "0.5.9",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.9.tgz",
"integrity": "sha512-8zkGu/sv5euxbjfZ/xmklqLyDGQSxsLqg8XOq88JW3cmJtzhCP8EtSJXlaKZnVO4beEaoiT9wj4eIoCQ9smwxA==",
"dependencies": {
"tslib": "^2.4.0"
"tslib": "2"
}
},
"node_modules/@hookform/resolvers": {
@ -3678,6 +3686,11 @@
}
}
},
"node_modules/decimal.js": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
"integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA=="
},
"node_modules/deep-is": {
"version": "0.1.4",
"license": "MIT"
@ -4884,13 +4897,14 @@
}
},
"node_modules/intl-messageformat": {
"version": "10.5.5",
"license": "BSD-3-Clause",
"version": "10.7.10",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.10.tgz",
"integrity": "sha512-hp7iejCBiJdW3zmOe18FdlJu8U/JsADSDiBPQhfdSeI8B9POtvPRvPh3nMlvhYayGMKLv6maldhR7y3Pf1vkpw==",
"dependencies": {
"@formatjs/ecma402-abstract": "1.17.3",
"@formatjs/fast-memoize": "2.2.0",
"@formatjs/icu-messageformat-parser": "2.7.1",
"tslib": "^2.4.0"
"@formatjs/ecma402-abstract": "2.3.1",
"@formatjs/fast-memoize": "2.2.5",
"@formatjs/icu-messageformat-parser": "2.9.7",
"tslib": "2"
}
},
"node_modules/invariant": {
@ -5497,6 +5511,14 @@
"version": "1.4.0",
"license": "MIT"
},
"node_modules/negotiator": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/next": {
"version": "14.0.2",
"license": "MIT",
@ -5541,6 +5563,26 @@
}
}
},
"node_modules/next-intl": {
"version": "3.26.0",
"resolved": "https://registry.npmjs.org/next-intl/-/next-intl-3.26.0.tgz",
"integrity": "sha512-gkamnHIANQzeW8xpTGRxd0xiOCztQhY8GDp79fgdlw0GioqrjTEfSWLhHkgaAtvHRbuh/ByJdwiEY5eNK9bUSQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/amannn"
}
],
"dependencies": {
"@formatjs/intl-localematcher": "^0.5.4",
"negotiator": "^1.0.0",
"use-intl": "^3.26.0"
},
"peerDependencies": {
"next": "^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0"
}
},
"node_modules/next-themes": {
"version": "0.2.1",
"license": "MIT",
@ -7114,6 +7156,18 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/use-intl": {
"version": "3.26.0",
"resolved": "https://registry.npmjs.org/use-intl/-/use-intl-3.26.0.tgz",
"integrity": "sha512-HGXmpjGlbEv1uFZPfm557LK8p/hv0pKF9UwnrJeHUTxQx6bUGzMgpmPRLCVY3zkr7hfjy4LPwQJfk4Fhnn+dIg==",
"dependencies": {
"@formatjs/fast-memoize": "^2.2.0",
"intl-messageformat": "^10.5.14"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0"
}
},
"node_modules/use-isomorphic-layout-effect": {
"version": "1.1.2",
"license": "MIT",

View File

@ -43,6 +43,7 @@
"jodit-react": "^4.0.25",
"js-cookie": "^3.0.5",
"next": "14.0.2",
"next-intl": "^3.26.0",
"next-themes": "^0.2.1",
"postcss": "8.4.31",
"react": "18.2.0",

24
store/language-store.tsx Normal file
View File

@ -0,0 +1,24 @@
import { create } from "zustand";
interface targetStore {
locale: string;
setLocale: (newTarget: string) => void;
}
const getInitialTarget = () => {
if (typeof localStorage !== "undefined") {
const stored = localStorage.getItem("locale");
const initial = stored ? JSON.parse(stored) : "id";
return initial;
}
};
const storedLanguage = create<targetStore>((set) => ({
locale: getInitialTarget(),
setLocale: (newTarget: string) => {
localStorage.setItem("locale", JSON.stringify(newTarget));
set({ locale: newTarget });
},
}));
export default storedLanguage;