Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enabling i18n feature for smaller screens #3556

Merged
merged 26 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d152a10
added langauge option in mobile navbar
devilkiller-ag Jan 8, 2025
57955e1
updated locale names
devilkiller-ag Jan 8, 2025
6879ec8
added functionality to highlight current language
devilkiller-ag Jan 8, 2025
eec0f74
added language icon
devilkiller-ag Jan 8, 2025
9e7a307
fixed nav items spacing
devilkiller-ag Jan 8, 2025
ac2f811
updated add translations doc
devilkiller-ag Jan 8, 2025
616595b
updated add translations doc
devilkiller-ag Jan 8, 2025
00edd38
Update components/icons/Language.tsx
devilkiller-ag Jan 21, 2025
3c64066
Merge branch 'asyncapi:master' into i18n-for-smaller-screen
devilkiller-ag Jan 21, 2025
2e48227
Merge branch 'master' into i18n-for-smaller-screen
akshatnema Jan 26, 2025
f2fd10f
Merge branch 'master' into i18n-for-smaller-screen
anshgoyalevil Jan 27, 2025
2e8e958
Merge branch 'asyncapi:master' into i18n-for-smaller-screen
devilkiller-ag Jan 31, 2025
18aaff7
refactor: revert localname from full form to first-two-letters
devilkiller-ag Jan 31, 2025
ecf4f1a
refactor: removed need to change locale display name at multiple places
devilkiller-ag Jan 31, 2025
a0e3fe7
fix: lint issues
devilkiller-ag Jan 31, 2025
86b09fc
chore: removed usage of lib in the translation doc
devilkiller-ag Jan 31, 2025
b9a7d0d
Merge branch 'master' into i18n-for-smaller-screen
asyncapi-bot Feb 2, 2025
9222342
Merge branch 'master' into i18n-for-smaller-screen
devilkiller-ag Feb 10, 2025
2547f5f
updated doc
devilkiller-ag Feb 10, 2025
e1e0e10
updated language name to full name in options
devilkiller-ag Feb 10, 2025
7efaed2
fixed linting issue
devilkiller-ag Feb 10, 2025
972252f
Merge branch 'master' into i18n-for-smaller-screen
anshgoyalevil Feb 11, 2025
e204776
refactor: moved langmap to next-i18next.config.js
devilkiller-ag Feb 11, 2025
da77462
Merge branch 'i18n-for-smaller-screen' of https://github.com/devilkil…
devilkiller-ag Feb 11, 2025
e835e66
updated adding translation readme
devilkiller-ag Feb 11, 2025
e9a68fa
Merge branch 'master' into i18n-for-smaller-screen
anshgoyalevil Feb 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions ADDING_TRANSLATIONS.md
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert the changes related to en -> english here and in i18n config file since URLs should be having smaller prefix

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay I will do that!

Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
We appreciate your valuable contributions to the AsyncAPI website, whether it's adding or improving existing translations.

## Table of contents <!-- omit in toc -->
- [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

Expand Down Expand Up @@ -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`.
devilkiller-ag marked this conversation as resolved.
Show resolved Hide resolved
- 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`.

Expand All @@ -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',
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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
Expand All @@ -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() {

Expand Down Expand Up @@ -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: [
Expand Down Expand Up @@ -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'.

Expand All @@ -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: {
Expand All @@ -294,7 +294,7 @@ module.exports = {
```
- Add new locale routing.

`lib/i18nPaths.js`
`utils/i18n.ts`
```diff
const i18nPaths = {
en: [
Expand Down
5 changes: 5 additions & 0 deletions components/icons/Icons.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -236,6 +237,10 @@ These are the icons used in the AsyncAPI website.
<InfoIcon />
</IconItem>

<IconItem name="Language">
<IconLanguage />
</IconItem>

<IconItem name="Light Bulb">
<IconLightBulb />
</IconItem>
Expand Down
24 changes: 24 additions & 0 deletions components/icons/Language.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<svg
xmlns='http://www.w3.org/2000/svg'
fill='none'
viewBox='0 0 24 24'
strokeWidth={1.5}
stroke='currentColor'
className={`size-5 ${className}`}
>
<path
strokeLinecap='round'
strokeLinejoin='round'
d='m10.5 21 5.25-11.25L21 21m-9-3h7.5M3 5.621a48.474 48.474 0 0 1 6-.371m0 0c1.12 0 2.233.038 3.334.114M9 5.25V3m3.334 2.364C11.176 10.658 7.69 15.08 3 17.502m9.334-12.138c.896.061 1.785.147 2.666.257m-4.589 8.495a18.023 18.023 0 0 1-3.827-5.802'
/>
</svg>
);
}
39 changes: 25 additions & 14 deletions components/languageSelector/LanguageSelect.tsx
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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 (
<select
data-testid='Select-form'
onChange={(ev) => onChange(ev.target.value)}
className={twMerge(
`form-select h-full py-0 px-3 pr-7 inline-flex justify-center rounded-md border border-gray-300 shadow-sm py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:border-gray-500 focus:outline-none focus:ring-0 focus:ring-black ${className}`
)}
value={selected}
>
{options.map((option, index) => (
<option key={index} value={option.value} data-testid='Option-form'>
{option.text}
</option>
))}
</select>
<div className='relative inline-block'>
<div className='relative flex items-center gap-2'>
{/* Display Icon Next to the Select Box */}
<IconLanguage className='pointer-events-none absolute left-3 text-gray-600' />
<select
data-testid='Select-form'
onChange={(ev) => onChange(ev.target.value)}
className={twMerge(
`form-select h-full px-10 pr-7 inline-flex justify-center rounded-md border border-gray-300 shadow-sm py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:border-gray-500 focus:outline-none focus:ring-0 focus:ring-black ${className}`
)}
value={selected}
>
{options.map((option, index) => (
<option key={index} value={option.value} data-testid='Option-form'>
{langMap[option.text.toLowerCase() as keyof typeof langMap] || option.text}
</option>
))}
</select>
</div>
</div>
);
}
40 changes: 38 additions & 2 deletions components/navigation/MobileNavMenu.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<string | null>(null);

/**
Expand All @@ -41,6 +52,8 @@ export default function MobileNavMenu({ onClickClose = () => {} }: MobileNavMenu
setOpen(menu);
}

const { langMap } = i18nextConfig;

return (
<div className='fixed inset-x-0 top-0 z-60 max-h-full origin-top-right overflow-y-auto py-2 transition lg:hidden'>
<div className='rounded-lg shadow-lg'>
Expand Down Expand Up @@ -104,7 +117,7 @@ export default function MobileNavMenu({ onClickClose = () => {} }: MobileNavMenu
</h4>
{open === 'community' && <MenuBlocks items={communityItems} />}
</div>
<div className='space-y-2 px-5 py-2' onClick={() => showMenu('others')} data-testid='MobileNav-others'>
<div className='space-y-2 px-5 pt-2' onClick={() => showMenu('others')} data-testid='MobileNav-others'>
<div className='grid gap-4'>
<div>
<h4 className='mb-4 flex justify-between font-medium text-gray-800'>
Expand All @@ -127,6 +140,29 @@ export default function MobileNavMenu({ onClickClose = () => {} }: MobileNavMenu
</div>
</div>
</div>
<div className='space-y-2 px-5 py-2' onClick={() => showMenu('language')}>
<div className='grid gap-4'>
<div>
<h4 className='mb-4 flex justify-between font-medium text-gray-800'>
<a className='flex cursor-pointer items-center gap-x-2'>
Language <IconLanguage />
</a>
<NavItemDropdown />
</h4>
{open === 'language' &&
uniqueLangs.map((lang) => (
<button
key={lang.key}
onClick={() => changeLanguage(lang.value.toLowerCase(), true)}
className={`mb-4 ml-2 block w-full rounded-lg py-1 text-start text-sm font-medium leading-6 text-gray-700 transition duration-150 ease-in-out hover:bg-gray-50 ${currentLanguage.toLowerCase() === lang.text.toLowerCase() ? 'text-secondary-500' : ''}`}
data-testid='MobileNav-language-item'
>
{langMap[lang.text.toLowerCase() as keyof typeof langMap] || lang.text}
</button>
))}
</div>
</div>
</div>
</div>
</div>
</div>
Expand Down
25 changes: 15 additions & 10 deletions components/navigation/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
anshgoyalevil marked this conversation as resolved.
Show resolved Hide resolved
*/
const getUniqueLangs = (): string[] => {
let pathnameWithoutLocale = pathname;
Expand All @@ -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) => ({
Expand Down Expand Up @@ -147,7 +145,7 @@ export default function NavBar({ className = '', hideLogo = false }: NavBarProps

return (
<div className={`bg-white ${className} z-50`}>
<div className='flex w-full items-center justify-between py-6 lg:justify-start lg:space-x-10'>
<div className='flex w-full items-center justify-between py-6 lg:justify-start lg:space-x-2'>
{!hideLogo && (
<div className='lg:w-auto lg:flex-1'>
<div className='flex'>
Expand Down Expand Up @@ -178,7 +176,7 @@ export default function NavBar({ className = '', hideLogo = false }: NavBarProps
</div>

<nav
className='hidden w-full space-x-6 lg:flex lg:items-center lg:justify-end xl:space-x-10'
className='hidden w-full space-x-4 lg:flex lg:items-center lg:justify-end xl:space-x-8'
data-testid='Navbar-main'
>
<div className='relative' onMouseLeave={() => showMenu(null)} ref={learningRef}>
Expand Down Expand Up @@ -233,7 +231,7 @@ export default function NavBar({ className = '', hideLogo = false }: NavBarProps
changeLanguage(value.toLowerCase(), true);
}}
className=''
selected={i18n.language ? i18n.language.toUpperCase() : 'EN'}
selected={i18n.language ? i18n.language : 'en'}
/>

<GithubButton
Expand All @@ -247,7 +245,14 @@ export default function NavBar({ className = '', hideLogo = false }: NavBarProps
</div>

{/* Mobile menu, show/hide based on mobile menu state. */}
{mobileMenuOpen && <MobileNavMenu onClickClose={() => setMobileMenuOpen(false)} />}
{mobileMenuOpen && (
<MobileNavMenu
onClickClose={() => setMobileMenuOpen(false)}
uniqueLangs={uniqueLangs}
currentLanguage={i18n.language ? i18n.language : 'en'}
changeLanguage={changeLanguage}
/>
)}
</div>
);
}
21 changes: 12 additions & 9 deletions next-i18next.config.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
module.exports = {
i18n: {
locales: ['en', 'de'],
defaultLocale : 'en',
namespaces: ['landing-page', 'common', 'tools'],
defaultNamespace: 'landing-page',
react: { useSuspense: false },// this line
},

};
i18n: {
locales: ['en', 'de'],
defaultLocale: 'en',
namespaces: ['landing-page', 'common', 'tools'],
defaultNamespace: 'landing-page',
react: { useSuspense: false },// this line
anshgoyalevil marked this conversation as resolved.
Show resolved Hide resolved
},
langMap: {
en: 'English',
de: 'Deutsch'
},
};
Loading