Skip to content

Commit

Permalink
feat: react-i18n setup for multi language support
Browse files Browse the repository at this point in the history
  • Loading branch information
AlkenD committed Jul 22, 2024
1 parent 17ab2f6 commit 3bd09e4
Show file tree
Hide file tree
Showing 16 changed files with 693 additions and 1,911 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
"@tauri-apps/api": "^1.5.6",
"class-variance-authority": "^0.7.0",
"framer-motion": "^11.2.10",
"i18next": "^23.12.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^15.0.0",
"react-router-dom": "^6.23.1",
"styled-components": "^6.1.11",
"swiper": "^11.1.4",
Expand Down
56 changes: 56 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 18 additions & 4 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { RouterProvider, createBrowserRouter } from "react-router-dom";
import HomePage from "./lib/pages/HomePage";
import BrowsePage from "./lib/pages/BrowsePage";
import LibraryPage from "./lib/pages/LibraryPage";
import { RouterProvider, createBrowserRouter } from "react-router-dom";
import ErrorPage from "./lib/pages/ErrorPage";
import getItemData from "./lib/api/getItemData";
import Navigation from "./lib/components/Navigation/Navigation";
import MovieInfoPage from "./lib/pages/MovieInfoPage";
import TvInfoPage from "./lib/pages/TvInfoPage";
import getSingleItemData from "./lib/api/getSingleItemData";
import "./i18n";
import "./index.css";
import ErrorPage from "./lib/pages/ErrorPage";

const App = () => {
const router = createBrowserRouter([
Expand Down Expand Up @@ -37,8 +40,12 @@ const App = () => {
path: "library",
element: <LibraryPage />,
loader: async () => {
const res = await getItemData("movies");
return res;
const resMovies = await getItemData("movies");
const resTv = await getItemData("tv");
return {
movies: resMovies,
tv: resTv,
};
},
},
{
Expand All @@ -60,6 +67,13 @@ const App = () => {
],
},
]);

const { i18n } = useTranslation();

useEffect(() => {
const lng = navigator.language;
i18n.changeLanguage(lng);
}, []);
return <RouterProvider router={router} />;
};

Expand Down
37 changes: 37 additions & 0 deletions src/i18n/enTranslations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const enTranslations = {
nav: {
items: {
home: "Home",
browse: "Browse",
library: "Library",
search: "Search",
},
userMenu: {
users: "Users",
removeUser: "Remove User",
settings: "Settings",
logout: "Logout",
guest: "Guest",
},
},
actions: {
playNow: "Play Now",
trailer: "Trailer",
bookmark: "Bookmark",
info: "Info",
search: "Search",
close: "Close",
open: "Open",
},
sliders: {
weeklyRecommendation: "Weekly Recommendation",
},
metadata: {
season: "Season",
seasons: "Seasons",
episode: "Episode",
episodes: "Episodes",
},
};

export default enTranslations;
18 changes: 18 additions & 0 deletions src/i18n/i18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import enTranslations from "./enTranslations";

i18n.use(initReactI18next).init({
resources: {
en: {
translation: enTranslations,
},
},
lng: "en",
fallbackLng: "en",
interpolation: {
escapeValue: false,
},
});

export default i18n;
1 change: 1 addition & 0 deletions src/i18n/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as i18n } from "./i18n";
10 changes: 5 additions & 5 deletions src/lib/components/Filters/LibraryFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const SelectMenu = ({ title }: any) => {

return (
<Listbox value={selectedPeople} onChange={setSelectedPeople} multiple>
<ListboxButton className="relative flex items-center justify-between w-full rounded-lg bg-neutral-800/80 py-1.5 pl-4 pr-2 h-10 text-left text-sm/6 text-white focus:outline-none data-[focus]:outline-2 data-[focus]:-outline-offset-2 data-[focus]:outline-white/25 shadow-[inset_0_1px_0_0_#ffffff1a]">
<ListboxButton className="relative flex items-center justify-between w-full rounded-lg bg-neutral-800/80 py-1.5 pl-4 pr-2 h-10 text-left text-sm/tight text-white focus:outline-none data-[focus]:outline-2 data-[focus]:-outline-offset-2 data-[focus]:outline-white/25 shadow-[inset_0_1px_0_0_#ffffff1a]">
<div className="flex items-center space-x-2">
<span>{title}</span> <Chip rounded>{selectedPeople.length}</Chip>
</div>
Expand All @@ -37,7 +37,7 @@ const SelectMenu = ({ title }: any) => {
<ListboxOption
key={person.id}
value={person}
className="group flex cursor-default items-center gap-2 rounded-lg py-1.5 px-3 select-none data-[focus]:bg-white/10"
className="group flex cursor-default items-center gap-2 rounded-lg py-1.5 px-3 select-none data-[focus]:bg-white/10 text-sm/tight"
>
<div className="invisible size-4 fill-white group-data-[selected]:visible h-6 w-6 p-1 flex justify-center items-center">
<HeroIcon iconName="CheckIcon" size="small" />
Expand Down Expand Up @@ -65,11 +65,11 @@ const LibraryFilter = () => {
<SelectMenu title="Category" />
<SelectMenu title="Genre" />
<SelectMenu title="Type" />
<SelectMenu title="Rating" />
<SelectMenu title="Year" />
</div>
<div className="flex space-x-4">
<SelectMenu title="Popularity" />
<SelectMenu title="Year" />
<SelectMenu title="Available Languages" />
<SelectMenu title="Available Subtitles" />
<SelectMenu title="Airing Status" />
<SelectMenu title="Country of Origin" />
</div>
Expand Down
87 changes: 46 additions & 41 deletions src/lib/components/Navigation/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { RouteConfig } from "../../types";
import HeroIcon from "../HeroIcon";
import Logo from "../Logo";
import NavbarMenu from "../Menus/NavbarMenu";
import { useTranslation } from "react-i18next";

interface NavbarProps {
routes: RouteConfig[];
Expand All @@ -15,47 +16,51 @@ interface SearchProps {
setIsSearch: (value: boolean) => void;
}

const Search = ({ isSearch, setIsSearch }: SearchProps) => (
<motion.div className="flex">
<motion.input
key="search-input"
initial={{ opacity: 0, width: 0 }}
variants={{
show: {
opacity: 1,
width: "300px",
},
hide: { opacity: 0, width: 0 },
}}
animate={isSearch ? "show" : "hide"}
className="rounded-full bg-white/20 indent-4"
/>
<button
onClick={() => setIsSearch(!isSearch)}
className={`pl-2 pr-4 py-2 flex items-center justify-center rounded-full hover:bg-white hover:text-black transition-colors overflow-hidden w-28 ml-2`}
>
{isSearch ? (
<span className="flex space-x-2">
<div className="h-6 w-6 flex justify-center items-center aspect-square">
<HeroIcon size="large" iconName="XMarkIcon" type="outline" />
</div>
<span>Close</span>
</span>
) : (
<span className="flex space-x-2">
<div className="h-6 w-6 flex justify-center items-center aspect-square">
<HeroIcon
size="large"
iconName="MagnifyingGlassIcon"
type="outline"
/>
</div>
<span>Search</span>
</span>
)}
</button>
</motion.div>
);
const Search = ({ isSearch, setIsSearch }: SearchProps) => {
const { t } = useTranslation();

return (
<motion.div className="flex">
<motion.input
key="search-input"
initial={{ opacity: 0, width: 0 }}
variants={{
show: {
opacity: 1,
width: "300px",
},
hide: { opacity: 0, width: 0 },
}}
animate={isSearch ? "show" : "hide"}
className="rounded-full bg-white/20 indent-4"
/>
<button
onClick={() => setIsSearch(!isSearch)}
className={`pl-2 pr-4 py-2 flex items-center justify-center rounded-full hover:bg-white hover:text-black transition-colors overflow-hidden w-28 ml-2`}
>
{isSearch ? (
<span className="flex space-x-2">
<div className="h-6 w-6 flex justify-center items-center aspect-square">
<HeroIcon size="large" iconName="XMarkIcon" type="outline" />
</div>
<span>{t("actions.close")}</span>
</span>
) : (
<span className="flex space-x-2">
<div className="h-6 w-6 flex justify-center items-center aspect-square">
<HeroIcon
size="large"
iconName="MagnifyingGlassIcon"
type="outline"
/>
</div>
<span>{t("actions.search")}</span>
</span>
)}
</button>
</motion.div>
);
};

const NavBar = ({ routes }: NavbarProps) => {
const [isOpen, setIsOpen] = useState(false);
Expand Down
Loading

0 comments on commit 3bd09e4

Please sign in to comment.