From a3dc30d57cb3dfc68b1b091dbaa4789224495cc5 Mon Sep 17 00:00:00 2001 From: Ashmit JaiSarita Gupta <43639341+devilkiller-ag@users.noreply.github.com> Date: Tue, 11 Feb 2025 22:09:37 +0530 Subject: [PATCH] feat: enabling i18n feature for smaller screens (#3556) * added langauge option in mobile navbar * updated locale names * added functionality to highlight current language * added language icon * fixed nav items spacing * updated add translations doc * updated add translations doc * Update components/icons/Language.tsx Co-authored-by: Ansh Goyal * refactor: revert localname from full form to first-two-letters * refactor: removed need to change locale display name at multiple places * fix: lint issues * chore: removed usage of lib in the translation doc * updated doc * updated language name to full name in options * fixed linting issue * refactor: moved langmap to next-i18next.config.js * updated adding translation readme --------- Co-authored-by: Ansh Goyal Co-authored-by: Akshat Nema <76521428+akshatnema@users.noreply.github.com> Co-authored-by: asyncapi-bot --- ADDING_TRANSLATIONS.md | 41 +++++++++++-------- components/icons/Icons.mdx | 5 +++ components/icons/Language.tsx | 24 +++++++++++ .../languageSelector/LanguageSelect.tsx | 39 +++++++++++------- components/navigation/MobileNavMenu.tsx | 40 +++++++++++++++++- components/navigation/NavBar.tsx | 25 ++++++----- next-i18next.config.js | 21 ++++++---- 7 files changed, 142 insertions(+), 53 deletions(-) create mode 100644 components/icons/Language.tsx diff --git a/ADDING_TRANSLATIONS.md b/ADDING_TRANSLATIONS.md index e4a26f9fefb1..6f43cca53279 100644 --- a/ADDING_TRANSLATIONS.md +++ b/ADDING_TRANSLATIONS.md @@ -3,10 +3,10 @@ We appreciate your valuable contributions to the AsyncAPI website, whether it's adding or improving existing translations. ## Table of contents -- [Improving existing translations:](#improving-existing-translations) -- [Adding translations to a partially localized page:](#adding-translations-to-a-partially-localized-page) -- [Adding translations to a new page:](#adding-translations-to-a-new-page) -- [Adding a new locale:](#adding-a-new-locale) +- [Improving existing translations](#improving-existing-translations) +- [Adding translations to a partially localized page](#adding-translations-to-a-partially-localized-page) +- [Adding translations to a new page](#adding-translations-to-a-new-page) +- [Adding a new locale](#adding-a-new-locale) ## Improving existing translations @@ -45,7 +45,7 @@ Use the translation hook with the key specified in the `locales` folder. Suppose the Landing Page has a button that is still in English when the language is set to German: - Navigate to the file where the component is defined. -- Import the `useTranslation` hook from `lib/i18n`. +- Import the `useTranslation` hook from `utils/i18n`. - Extract the translation function from the hook `const { t } = useTranslation();`. - Use it to pass the key of the required translation value. Make sure to add the required key to the `locales` folder according to the page's scope. In this example, we are adding translation for a button, since all translation keys related to buttons need to be specified in `common.json`. @@ -54,7 +54,7 @@ Example: `ICSFileButton.js` ```diff ... -+ import { useTranslation } from '../../lib/i18n'; ++ import { useTranslation } from '../../utils/i18n'; export default function ICSFButton({ - text = 'Download ICS File', @@ -131,10 +131,10 @@ The process for adding translations to a page that is not yet available in any e **4. Configure i18n routing** After adding a new internationalized page, test it to sure the page is being served on the website when someone visits it. - - Replace the `next/link` component with the `LinkComponent` from `components/link.js` in the files where the page's `href` is being referenced. - - Make sure to add the exact same `href` to the `lib/i18nPaths.js` in the respective locales which support that `href`. + - Replace the `next/link` component with the `LinkComponent` from `components/link.tsx` in the files where the page's `href` is being referenced. + - Make sure to add the exact same `href` to the `utils/i18n.ts` in the respective locales which support that `href`. - For example, if you want to translate the `pages/newsletter/index.js` page, so that if someone visits `asyncapi.com/de/newsletter`, it shows the page in the `German` locale. + For example, if you want to translate the `pages/newsletter.tsx` page, so that if someone visits `asyncapi.com/de/newsletter`, it shows the page in the `German` locale. - Add new `JSON` files to the `locales/en` and `locales/de` folder. @@ -167,7 +167,7 @@ After adding a new internationalized page, test it to sure the page is being ser }; ``` - - Copy and add static site functions to the `newsletter/index.js` page. + - Copy and add static site functions to the `newsletter.tsx` page. `pages` folder directory structure ```diff @@ -179,14 +179,14 @@ After adding a new internationalized page, test it to sure the page is being ser ┗ index.js ``` - `newsletter/index.js` + `newsletter.tsx` ```diff ... + import { + getAllLanguageSlugs, + getLanguage, + useTranslation - + } from "../../lib/i18n"; + + } from "../../utils/i18n"; export default function NewsletterIndexPage() { @@ -217,7 +217,7 @@ After adding a new internationalized page, test it to sure the page is being ser - Add custom route `LinkComponent` wherever the `next/link` is used for routing to the `/newsletter` href. - `lib/i18nPaths.js` + `utils/i18n.ts` ```diff const i18nPaths = { en: [ @@ -252,12 +252,12 @@ If you want to add a new locale like `fr` to serve pages in the French locale on - Copy the existing `JSON` files present in the `en` folder. Change the values of those translation keys according to the new localization. **2. Modify i18n configuration** - - Navigate to the `next-i18next-static-site.config.js` file in the root of the project folder. + - Navigate to the `next-i18next.config.js` file in the root of the project folder. - Add the name of the newly added `locale` to the `languages` array. **3. Configure i18n routing** After adding a new internationalized page, ensure it is being served on the website when someone visits. - - Make sure to add the same `href` to the `lib/i18nPaths.js` in the respective locales supporting that `href`. + - Make sure to add the same `href` to the `utils/i18n.ts` in the respective locales supporting that `href`. If you have added the 'fr' locale and translated the 'tools/cli' page, clicking 'Tools -> CLI' in the navigation menu will redirect the user to 'asyncapi.com/fr/tools/cli'. @@ -278,9 +278,9 @@ If you have added the 'fr' locale and translated the 'tools/cli' page, clicking + ┃ ┗ tools.json ``` -- Change the `next-i18next-static-site.config.js` config. +- Change the `next-i18next.config.js` config. -`next-i18next-static-site.config.js` +`next-i18next.config.js` ```diff module.exports = { i18n: { @@ -290,11 +290,16 @@ module.exports = { namespaces: ["landing-page", "common", "tools"], defaultNamespace: "landing-page", }, + langMap: { + en: 'English', + de: 'Deutsch', ++ fr: 'French', + }, }; ``` - Add new locale routing. -`lib/i18nPaths.js` +`utils/i18n.ts` ```diff const i18nPaths = { en: [ diff --git a/components/icons/Icons.mdx b/components/icons/Icons.mdx index cb6496377a38..81743e573ba2 100644 --- a/components/icons/Icons.mdx +++ b/components/icons/Icons.mdx @@ -30,6 +30,7 @@ import IconGuide from './Guide'; import IconHome from './Home'; import IconHub from './Hub'; import InfoIcon from './InfoIcon'; +import IconLanguage from './Language'; import IconLightBulb from './LightBulb'; import IconLinkedIn from './LinkedIn'; import IconLoupe from './Loupe'; @@ -236,6 +237,10 @@ These are the icons used in the AsyncAPI website. + + + + diff --git a/components/icons/Language.tsx b/components/icons/Language.tsx new file mode 100644 index 000000000000..01847ceeaaae --- /dev/null +++ b/components/icons/Language.tsx @@ -0,0 +1,24 @@ +import React from 'react'; + +/* eslint-disable max-len */ +/** + * @description Language Icon for language selector component + */ +export default function IconLanguage({ className = '' }) { + return ( + + + + ); +} diff --git a/components/languageSelector/LanguageSelect.tsx b/components/languageSelector/LanguageSelect.tsx index d2d4f0750514..1f3e3db023d8 100644 --- a/components/languageSelector/LanguageSelect.tsx +++ b/components/languageSelector/LanguageSelect.tsx @@ -1,7 +1,10 @@ import React from 'react'; import { twMerge } from 'tailwind-merge'; +import i18nextConfig from '@/next-i18next.config'; + import type { SelectProps } from '../form/Select'; +import IconLanguage from '../icons/Language'; /** * @description LanguageSelect component for selecting a language. @@ -11,20 +14,28 @@ import type { SelectProps } from '../form/Select'; * @param {string} props.selected - The currently selected option value. */ export default function LanguageSelect({ className = '', onChange = () => {}, options = [], selected }: SelectProps) { + const { langMap } = i18nextConfig; + return ( - +
+
+ {/* Display Icon Next to the Select Box */} + + +
+
); } diff --git a/components/navigation/MobileNavMenu.tsx b/components/navigation/MobileNavMenu.tsx index 8b0ccd740374..56f7d47dd728 100644 --- a/components/navigation/MobileNavMenu.tsx +++ b/components/navigation/MobileNavMenu.tsx @@ -1,7 +1,10 @@ import Link from 'next/link'; import React, { useState } from 'react'; +import i18nextConfig from '@/next-i18next.config'; + import { SearchButton } from '../AlgoliaSearch'; +import IconLanguage from '../icons/Language'; import NavItemDropdown from '../icons/NavItemDropdown'; import SearchIcon from '../icons/SearchIcon'; import AsyncAPILogo from '../logos/AsyncAPILogo'; @@ -19,13 +22,21 @@ interface MenuItem { interface MobileNavMenuProps { onClickClose?: () => void; + uniqueLangs: { key: string; text: string; value: string }[]; + currentLanguage: string; + changeLanguage: (locale: string, langPicker: boolean) => void; } /** * @description MobileNavMenu component for displaying a responsive navigation menu on mobile devices. * @param {MobileNavMenuProps} props - The props for the MobileNavMenu component. */ -export default function MobileNavMenu({ onClickClose = () => {} }: MobileNavMenuProps) { +export default function MobileNavMenu({ + onClickClose = () => {}, + uniqueLangs, + currentLanguage, + changeLanguage +}: MobileNavMenuProps) { const [open, setOpen] = useState(null); /** @@ -41,6 +52,8 @@ export default function MobileNavMenu({ onClickClose = () => {} }: MobileNavMenu setOpen(menu); } + const { langMap } = i18nextConfig; + return (
@@ -104,7 +117,7 @@ export default function MobileNavMenu({ onClickClose = () => {} }: MobileNavMenu {open === 'community' && }
-
showMenu('others')} data-testid='MobileNav-others'> +
showMenu('others')} data-testid='MobileNav-others'>

@@ -127,6 +140,29 @@ export default function MobileNavMenu({ onClickClose = () => {} }: MobileNavMenu

+
showMenu('language')}> +
+
+

+ + Language + + +

+ {open === 'language' && + uniqueLangs.map((lang) => ( + + ))} +
+
+
diff --git a/components/navigation/NavBar.tsx b/components/navigation/NavBar.tsx index d0fd2ca6fa02..28b6917001bb 100644 --- a/components/navigation/NavBar.tsx +++ b/components/navigation/NavBar.tsx @@ -43,7 +43,7 @@ export default function NavBar({ className = '', hideLogo = false }: NavBarProps /** * Retrieves unique language options based on the current path and i18nPaths configuration. * - * @returns {string[]} - An array of unique language options in uppercase. + * @returns {string[]} - An array of unique language options with first letter in uppercase. */ const getUniqueLangs = (): string[] => { let pathnameWithoutLocale = pathname; @@ -54,12 +54,10 @@ export default function NavBar({ className = '', hideLogo = false }: NavBarProps } // Filter unique languages based on i18nPaths that include the modified pathnameWithoutLocale - const uniqueLangs = Object.keys(i18nPaths) - .filter((lang) => i18nPaths[lang].includes(pathnameWithoutLocale)) - .map((lang) => lang.toUpperCase()); + const uniqueLangs = Object.keys(i18nPaths).filter((lang) => i18nPaths[lang].includes(pathnameWithoutLocale)); - // If no unique languages are found, default to ["EN"] - return uniqueLangs.length === 0 ? ['EN'] : uniqueLangs; + // If no unique languages are found, default to ['en'] + return uniqueLangs.length === 0 ? ['en'] : uniqueLangs; }; const uniqueLangs = getUniqueLangs().map((lang) => ({ @@ -147,7 +145,7 @@ export default function NavBar({ className = '', hideLogo = false }: NavBarProps return (
-
+
{!hideLogo && (
@@ -178,7 +176,7 @@ export default function NavBar({ className = '', hideLogo = false }: NavBarProps