From 8176817030f05c9521ce325ee02c8392dc3d59b2 Mon Sep 17 00:00:00 2001 From: Sonny Date: Fri, 17 May 2024 19:36:53 +0200 Subject: [PATCH] feat: use existing translation on all pages --- config/inertia.ts | 8 ++- .../collection/header/collection_controls.tsx | 53 ++++++++++--------- .../collection/list/collection_list.tsx | 10 +++- .../dashboard/link/link_controls.tsx | 10 ++-- .../side_nav/favorite/favorite_list.tsx | 21 +++++--- .../dashboard/side_nav/side_navigation.tsx | 8 +-- inertia/components/form/form_collection.tsx | 10 ++-- inertia/components/lang_selector.tsx | 28 ++++++++++ inertia/components/layouts/form_layout.tsx | 53 ++++++++++--------- inertia/components/navbar/navbar.tsx | 18 +++++-- inertia/components/settings/modal.tsx | 13 +++-- inertia/constants/index.ts | 1 + inertia/i18n/index.ts | 8 +++ inertia/i18n/locales/en/common.json | 20 ++++--- inertia/i18n/locales/fr/common.json | 14 +++-- inertia/pages/collections/create.tsx | 4 +- inertia/pages/links/create.tsx | 12 +++-- package-lock.json | 8 +-- package.json | 2 +- 19 files changed, 197 insertions(+), 104 deletions(-) create mode 100644 inertia/components/lang_selector.tsx create mode 100644 inertia/constants/index.ts diff --git a/config/inertia.ts b/config/inertia.ts index 357ec89..992f43d 100644 --- a/config/inertia.ts +++ b/config/inertia.ts @@ -1,4 +1,7 @@ -import { PREFER_DARK_THEME } from '#constants/session'; +import { + DARK_THEME_DEFAULT_VALUE, + PREFER_DARK_THEME, +} from '#constants/session'; import { defineConfig } from '@adonisjs/inertia'; export default defineConfig({ @@ -12,7 +15,8 @@ export default defineConfig({ */ sharedData: { errors: (ctx) => ctx.session?.flashMessages.get('errors'), - preferDarkTheme: (ctx) => ctx.session?.get(PREFER_DARK_THEME, true), + preferDarkTheme: (ctx) => + ctx.session?.get(PREFER_DARK_THEME, DARK_THEME_DEFAULT_VALUE), auth: async (ctx) => { await ctx.auth?.check(); return { diff --git a/inertia/components/dashboard/collection/header/collection_controls.tsx b/inertia/components/dashboard/collection/header/collection_controls.tsx index 7aec8ab..3abed74 100644 --- a/inertia/components/dashboard/collection/header/collection_controls.tsx +++ b/inertia/components/dashboard/collection/header/collection_controls.tsx @@ -1,5 +1,6 @@ import type Collection from '#models/collection'; import { route } from '@izzyjs/route/client'; +import { useTranslation } from 'react-i18next'; import { BsThreeDotsVertical } from 'react-icons/bs'; import { GoPencil } from 'react-icons/go'; import { IoIosAddCircleOutline } from 'react-icons/io'; @@ -8,30 +9,34 @@ import Dropdown from '~/components/common/dropdown/dropdown'; import { DropdownItemLink } from '~/components/common/dropdown/dropdown_item'; import { appendCollectionId } from '~/lib/navigation'; -const CollectionControls = ({ +export default function CollectionControls({ collectionId, }: { collectionId: Collection['id']; -}) => ( - } svgSize={18}> - - Add - - - Edit - - - Delete - - -); - -export default CollectionControls; +}) { + const { t } = useTranslation('common'); + return ( + } svgSize={18}> + + {t('link.create')} + + + {t('collection.edit')} + + + {t('collection.delete')} + + + ); +} diff --git a/inertia/components/dashboard/collection/list/collection_list.tsx b/inertia/components/dashboard/collection/list/collection_list.tsx index c5afb4c..fec5897 100644 --- a/inertia/components/dashboard/collection/list/collection_list.tsx +++ b/inertia/components/dashboard/collection/list/collection_list.tsx @@ -1,4 +1,5 @@ import styled from '@emotion/styled'; +import { useTranslation } from 'react-i18next'; import CollectionItem from '~/components/dashboard/collection/list/collection_item'; import CollectionListContainer from '~/components/dashboard/collection/list/collection_list_container'; import useActiveCollection from '~/hooks/use_active_collection'; @@ -17,7 +18,10 @@ const SideMenu = styled.nav(({ theme }) => ({ const CollectionLabel = styled.p(({ theme }) => ({ color: theme.colors.grey, + marginBlock: '0.35em', + paddingInline: '15px', })); + const CollectionListStyle = styled.div({ padding: '1px', paddingRight: '5px', @@ -29,6 +33,7 @@ const CollectionListStyle = styled.div({ }); export default function CollectionList() { + const { t } = useTranslation('common'); const { collections } = useCollections(); const { activeCollection, setActiveCollection } = useActiveCollection(); @@ -60,7 +65,10 @@ export default function CollectionList() { return ( - Collections • {collections.length} + + {t('collection.collections', { count: collections.length })} •{' '} + {collections.length} + {collections.map((collection) => ( diff --git a/inertia/components/dashboard/link/link_controls.tsx b/inertia/components/dashboard/link/link_controls.tsx index 85ceca6..5b8fa3a 100644 --- a/inertia/components/dashboard/link/link_controls.tsx +++ b/inertia/components/dashboard/link/link_controls.tsx @@ -3,6 +3,7 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { route } from '@izzyjs/route/client'; import { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; import { AiFillStar, AiOutlineStar } from 'react-icons/ai'; import { BsThreeDotsVertical } from 'react-icons/bs'; import { GoPencil } from 'react-icons/go'; @@ -22,6 +23,7 @@ const StartItem = styled(DropdownItemButton)(({ theme }) => ({ export default function LinkControls({ link }: { link: Link }) { const theme = useTheme(); + const { t } = useTranslation('common'); const { collections, setCollections } = useCollections(); const toggleFavorite = useCallback( @@ -74,11 +76,11 @@ export default function LinkControls({ link }: { link: Link }) { {!link.favorite ? ( <> - Add to favorites + {t('add-favorite')} ) : ( <> - Remove from favorites + {t('remove-favorite')} )} @@ -88,7 +90,7 @@ export default function LinkControls({ link }: { link: Link }) { link.collectionId )} > - Edit + {t('link.edit')} - Delete + {t('link.delete')} ); diff --git a/inertia/components/dashboard/side_nav/favorite/favorite_list.tsx b/inertia/components/dashboard/side_nav/favorite/favorite_list.tsx index fc34bfa..306b7ed 100644 --- a/inertia/components/dashboard/side_nav/favorite/favorite_list.tsx +++ b/inertia/components/dashboard/side_nav/favorite/favorite_list.tsx @@ -1,4 +1,5 @@ import styled from '@emotion/styled'; +import { useTranslation } from 'react-i18next'; import TextEllipsis from '~/components/common/text_ellipsis'; import LinkFavicon from '~/components/dashboard/link/link_favicon'; import FavoriteListContainer from '~/components/dashboard/side_nav/favorite/favorite_container'; @@ -7,13 +8,18 @@ import useFavorites from '~/hooks/use_favorites'; const FavoriteLabel = styled.p(({ theme }) => ({ color: theme.colors.grey, + marginBlock: '0.35em', + paddingInline: '15px', })); -const NoFavorite = () => ( - - Your favorites will appear here - -); +const NoFavorite = () => { + const { t } = useTranslation('common'); + return ( + + {t('favorites-appears-here')} + + ); +}; const FavoriteListStyle = styled.div({ padding: '1px', @@ -26,6 +32,7 @@ const FavoriteListStyle = styled.div({ }); export default function FavoriteList() { + const { t } = useTranslation('common'); const { favorites } = useFavorites(); if (favorites.length === 0) { return ; @@ -33,8 +40,8 @@ export default function FavoriteList() { return ( - - Favorites • {favorites.length} + + {t('favorite')} • {favorites.length} {favorites.map(({ id, name, url }) => ( diff --git a/inertia/components/dashboard/side_nav/side_navigation.tsx b/inertia/components/dashboard/side_nav/side_navigation.tsx index 16a3b2c..1432f6e 100644 --- a/inertia/components/dashboard/side_nav/side_navigation.tsx +++ b/inertia/components/dashboard/side_nav/side_navigation.tsx @@ -1,5 +1,6 @@ import styled from '@emotion/styled'; import { route } from '@izzyjs/route/client'; +import { useTranslation } from 'react-i18next'; import { AiOutlineFolderAdd } from 'react-icons/ai'; import { IoAdd } from 'react-icons/io5'; import { MdOutlineAdminPanelSettings } from 'react-icons/md'; @@ -30,13 +31,14 @@ const AddButton = styled(ItemLink)(({ theme }) => ({ })); export default function SideNavigation() { + const { t } = useTranslation('common'); const { activeCollection } = useActiveCollection(); return (
- Administrator + {t('admin')} - Create link + {t('link.create')} - Create collection + {t('collection.create')}
diff --git a/inertia/components/form/form_collection.tsx b/inertia/components/form/form_collection.tsx index 1e1a9d6..1239380 100644 --- a/inertia/components/form/form_collection.tsx +++ b/inertia/components/form/form_collection.tsx @@ -4,6 +4,7 @@ import TextBox from '~/components/common/form/textbox'; import BackToDashboard from '~/components/common/navigation/back_to_dashboard'; import FormLayout from '~/components/layouts/form_layout'; import { Visibility } from '../../../app/enums/visibility'; +import { useTranslation } from 'react-i18next'; export type FormCollectionData = { name: string; @@ -30,6 +31,7 @@ export default function FormCollection({ setData: (name: string, value: string) => void; handleSubmit: () => void; }) { + const { t } = useTranslation('common'); const handleOnCheck = ({ target }: ChangeEvent) => setData( 'visibility', @@ -50,8 +52,8 @@ export default function FormCollection({ > ) => { + i18n.changeLanguage(target.value); + localStorage.setItem(LS_LANG_KEY, target.value); + }; + + return ( + + ); +} diff --git a/inertia/components/layouts/form_layout.tsx b/inertia/components/layouts/form_layout.tsx index ed4b627..e521474 100644 --- a/inertia/components/layouts/form_layout.tsx +++ b/inertia/components/layouts/form_layout.tsx @@ -1,13 +1,16 @@ import styled from '@emotion/styled'; -import { Link } from '@inertiajs/react'; +import { Head, Link } from '@inertiajs/react'; import { route } from '@izzyjs/route/client'; import { FormEvent, ReactNode } from 'react'; +import { useTranslation } from 'react-i18next'; import Button from '~/components/common/form/_button'; import Form from '~/components/common/form/_form'; +import TransitionLayout from '~/components/layouts/_transition_layout'; +import i18n from '~/i18n'; import { appendCollectionId } from '~/lib/navigation'; import BaseLayout from './_base_layout'; -const FormLayoutStyle = styled.div(({ theme }) => ({ +const FormLayoutStyle = styled(TransitionLayout)(({ theme }) => ({ height: 'fit-content', width: theme.media.mobile, maxWidth: '100%', @@ -30,31 +33,33 @@ interface FormLayoutProps { collectionId?: string; } -const FormLayout = ({ +export default function FormLayout({ title, children, canSubmit, handleSubmit, - textSubmitButton = 'Confirm', + textSubmitButton = i18n.t('common:confirm'), disableHomeLink = false, collectionId, -}: FormLayoutProps) => ( - - -

{title}

-
- {children} - -
- {!disableHomeLink && ( - - ← Back to home - - )} -
-
-); - -export default FormLayout; +}: FormLayoutProps) { + const { t } = useTranslation('common'); + return ( + + + +

{title}

+
+ {children} + +
+ {!disableHomeLink && ( + + {t('back-home')} + + )} +
+
+ ); +} diff --git a/inertia/components/navbar/navbar.tsx b/inertia/components/navbar/navbar.tsx index 9e99544..5b513be 100644 --- a/inertia/components/navbar/navbar.tsx +++ b/inertia/components/navbar/navbar.tsx @@ -1,6 +1,7 @@ import styled from '@emotion/styled'; import { Link } from '@inertiajs/react'; import { route } from '@izzyjs/route/client'; +import { useTranslation } from 'react-i18next'; import { IoIosLogOut } from 'react-icons/io'; import Dropdown from '~/components/common/dropdown/dropdown'; import { @@ -49,7 +50,9 @@ const UserCard = styled.div({ }); export default function Navbar() { + const { t } = useTranslation('common'); const { isAuthenticated, user } = useUser(); + return ( @@ -83,7 +91,9 @@ export default function Navbar() { } function ProfileDropdown() { + const { t } = useTranslation('common'); const { user } = useUser(); + return ( - Logout + {t('logout')} ); diff --git a/inertia/components/settings/modal.tsx b/inertia/components/settings/modal.tsx index 35b2fc7..1a6faef 100644 --- a/inertia/components/settings/modal.tsx +++ b/inertia/components/settings/modal.tsx @@ -1,5 +1,7 @@ +import { useTranslation } from 'react-i18next'; import { BsGear } from 'react-icons/bs'; import Modal from '~/components/common/modal/modal'; +import LangSelector from '~/components/lang_selector'; import ThemeSwitcher from '~/components/theme_switcher'; import useToggle from '~/hooks/use_modal'; @@ -9,19 +11,16 @@ export default function ModalSettings({ // TODO: fix this :() openItem: any; }) { + const { t } = useTranslation('common'); const { isShowing, open, close } = useToggle(); return ( <> - Settings + {t('settings')} - - Modal settings -
- + +
diff --git a/inertia/constants/index.ts b/inertia/constants/index.ts new file mode 100644 index 0000000..2bcd071 --- /dev/null +++ b/inertia/constants/index.ts @@ -0,0 +1 @@ +export const LS_LANG_KEY = 'language'; diff --git a/inertia/i18n/index.ts b/inertia/i18n/index.ts index 966569a..b499bfb 100644 --- a/inertia/i18n/index.ts +++ b/inertia/i18n/index.ts @@ -12,6 +12,7 @@ import frResourceLogin from './locales/fr/login.json'; import frResourcePrivacy from './locales/fr/privacy.json'; import frResourceTerms from './locales/fr/terms.json'; +import { LS_LANG_KEY } from '~/constants'; import enResourceAbout from './locales/en/about.json'; import enResourceAdmin from './locales/en/admin.json'; import enResourceCommon from './locales/en/common.json'; @@ -51,9 +52,16 @@ export const resources = { }, } as const; +export const languages = ['en', 'fr'] as const; + +const lng = + typeof window !== 'undefined' + ? localStorage.getItem(LS_LANG_KEY) || undefined + : undefined; i18n.use(initReactI18next).init({ returnNull: false, resources, + lng, fallbackLng: 'en', interpolation: { escapeValue: false, diff --git a/inertia/i18n/locales/en/common.json b/inertia/i18n/locales/en/common.json index 0195212..aed1f13 100644 --- a/inertia/i18n/locales/en/common.json +++ b/inertia/i18n/locales/en/common.json @@ -2,7 +2,7 @@ "slogan": "Manage your links in the best possible way", "confirm": "Confirm", "cancel": "Cancel", - "back-home": "← Back to home page", + "back-home": "← Back to home", "logout": "Logout", "login": "Login", "link": { @@ -12,25 +12,29 @@ "description": "Link description", "create": "Create a link", "edit": "Edit a link", - "remove": "Delete a link", - "remove-confirm": "Confirm deletion?" + "delete": "Delete a link", + "delete-confirm": "Confirm deletion?" }, "collection": { - "collections": "Collections", - "collection": "Collection", + "collections": "Collection", + "collections_other": "Collections", "name": "Collection name", "description": "Collection description", "no-description": "No description", "visibility": "Public", "create": "Create a collection", "edit": "Edit a collection", - "remove": "Delete a collection", - "remove-confirm": "Confirm deletion?", - "remove-description": "You must delete all links in this collection before you can delete this collection." + "delete": "Delete a collection", + "delete-confirm": "Confirm deletion?", + "delete-description": "You must delete all links in this collection before you can delete this collection." }, "favorite": "Favorite", + "add-favorite": "Add to favorites", + "remove-favorite": "Remove from favorites", + "favorites-appears-here": "Your favorites will appear here", "no-item-found": "No item found", "search": "Search", + "admin": "Administrator", "avatar": "{{name}}'s avatar", "generic-error": "Something went wrong", "generic-error-description": "An error has occurred, if this happens again please create an issue with as much detail as possible.", diff --git a/inertia/i18n/locales/fr/common.json b/inertia/i18n/locales/fr/common.json index 8819966..cac13d4 100644 --- a/inertia/i18n/locales/fr/common.json +++ b/inertia/i18n/locales/fr/common.json @@ -12,8 +12,8 @@ "description": "Description du lien", "create": "Créer un lien", "edit": "Modifier un lien", - "remove": "Supprimer un lien", - "remove-confirm": "Confirmer la suppression ?" + "delete": "Supprimer un lien", + "delete-confirm": "Confirmer la suppression ?" }, "collection": { "collections": "Collection", @@ -25,12 +25,16 @@ "no-description": "Aucune description", "create": "Créer une collection", "edit": "Modifier une collection", - "remove": "Supprimer une collection", - "remove-confirm": "Confirmer la suppression ?", - "remove-description": "Vous devez supprimer tous les liens de cette collection avant de pouvoir supprimer cette collection" + "delete": "Supprimer une collection", + "delete-confirm": "Confirmer la suppression ?", + "delete-description": "Vous devez supprimer tous les liens de cette collection avant de pouvoir supprimer cette collection" }, "favorite": "Favoris", + "add-favorite": "Ajouter aux favoris", + "remove-favorite": "Retirer des favoris", + "favorites-appears-here": "Vos favoris apparaîtront ici", "no-item-found": "Aucun élément trouvé", + "admin": "Administrateur", "search": "Rechercher", "avatar": "Avatar de {{name}}", "generic-error": "Une erreur est survenue", diff --git a/inertia/pages/collections/create.tsx b/inertia/pages/collections/create.tsx index 6d9341f..e8a126c 100644 --- a/inertia/pages/collections/create.tsx +++ b/inertia/pages/collections/create.tsx @@ -4,12 +4,14 @@ import FormCollection, { FormCollectionData, } from '~/components/form/form_collection'; import { Visibility } from '../../../app/enums/visibility'; +import { useTranslation } from 'react-i18next'; export default function CreateCollectionPage({ disableHomeLink, }: { disableHomeLink: boolean; }) { + const { t } = useTranslation('common'); const { data, setData, post, processing } = useForm({ name: '', description: '', @@ -24,7 +26,7 @@ export default function CreateCollectionPage({ return ( - +