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

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
82 changes: 41 additions & 41 deletions ADDING_TRANSLATIONS.md
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 All @@ -18,12 +18,12 @@ The file `common.json` contains common translation keys such as buttons and CTAs

```
📦locales
┣ 📂de
┣ 📂deutsch
┃ ┣ 📜common.json
┃ ┣ 📜landing-page.json
┃ ┗ 📜tools.json
┃ ┗ 📜....json
┗ 📂en
┗ 📂english
┃ ┣ 📜common.json
┃ ┣ 📜landing-page.json
┃ ┗ 📜tools.json
Expand All @@ -32,7 +32,7 @@ The file `common.json` contains common translation keys such as buttons and CTAs

To modify a `Landing Page`'s heading:
- Navigate to the `locales` folder.
- Select a language, e.g. `de` (German) - go to the `de` folder.
- Select a language, e.g. `deutsch` (German) - go to the `deutsch` folder.
- Open `landing-page.json`.
- Change the values according to your needs.
- Create a pull request with the changes.
Expand All @@ -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`.

Expand Down Expand Up @@ -74,14 +74,14 @@ export default function ICSFButton({
}
```

`en/common.json`
`english/common.json`
```diff
{
+ "icsFileBtn": "Download ICS File",
}
```

`de/common.json`
`deutsch/common.json`
```diff
{
+ "icsFileBtn": "ICS-Datei herunterladen",
Expand Down Expand Up @@ -131,22 +131,22 @@ 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/deutsch/newsletter`, it shows the page in the `German` locale.

- Add new `JSON` files to the `locales/en` and `locales/de` folder.
- Add new `JSON` files to the `locales/english` and `locales/deutsch` folder.

`locales` folder directory structure
```diff
locales
de
deutsch
┃ ┣ common.json
┃ ┣ landing-page.json
+ ┃ ┣ newsletter.json
┃ ┗ tools.json
en
english
┃ ┣ common.json
┃ ┣ landing-page.json
+ ┃ ┣ newsletter.json
Expand All @@ -158,16 +158,16 @@ After adding a new internationalized page, test it to sure the page is being ser
```diff
module.exports = {
i18n: {
languages: ["en", "de"],
defaultLanguage: "en",
languages: ["english", "deutsch"],
defaultLanguage: "english",
- namespaces: ["landing-page", "common", "tools"],
+ namespaces: ["landing-page", "common", "tools", "newsletter"],
defaultNamespace: "landing-page",
},
};
```

- 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,7 +179,7 @@ After adding a new internationalized page, test it to sure the page is being ser
┗ index.js
```

`newsletter/index.js`
`newsletter.tsx`
```diff
...
+ import {
Expand Down Expand Up @@ -217,14 +217,14 @@ 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: [
english: [
"/tools/cli"
+ "/newsletter"
],
de: [
deutsch: [
"/tools/cli"
+ "/newsletter"
]
Expand All @@ -241,69 +241,69 @@ You are now done with adding the localization to the `newsletter` page.

AsyncAPI welcomes people from all over the world.

There exist a few locales like `en` (English) and `de` (German) which have available localizations present.
There exist a few locales like `english` (English) and `deutsch` (German) which have available localizations present.

If you want to add a new locale like `fr` to serve pages in the French locale on the AsyncAPI website, follow these steps.
If you want to add a new locale like `french` to serve pages in the French locale on the AsyncAPI website, follow these steps.

**1. Create new JSON Files**
- Navigate to the `locales` folder.
- Create a new folder with the name of the locale you want to introduce.
- Create new `JSON` files with the same name as present in each of the other `locales` folders.
- Copy the existing `JSON` files present in the `en` folder. Change the values of those translation keys according to the new localization.
- Copy the existing `JSON` files present in the `english` 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'.
If you have added the 'french' locale and translated the 'tools/cli' page, clicking 'Tools -> CLI' in the navigation menu will redirect the user to 'asyncapi.com/french/tools/cli'.

`locales` folder structure
```diff
locales
de
deutsch
┃ ┣ common.json
┃ ┣ landing-page.json
┃ ┗ tools.json
en
english
┃ ┣ common.json
┃ ┣ landing-page.json
┃ ┗ tools.json
+ ┗ fr
+ ┗ french
+ ┃ ┣ common.json
+ ┃ ┣ landing-page.json
+ ┃ ┗ 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: {
- languages: ["en", "de"],
+ languages: ["en", "de", "fr"],
defaultLanguage: "en",
- languages: ["english", "deutsch"],
+ languages: ["english", "deutsch", "french"],
defaultLanguage: "english",
Comment on lines +255 to +289
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Documentation inconsistency confirmed

The documentation refers to two different configuration files:

  • next-i18next-static-site.config.js (incorrect reference)
  • next-i18next.config.js (actual file)

The correct configuration file name is next-i18next.config.js, as verified in the repository. The reference to next-i18next-static-site.config.js should be updated to maintain consistency.

🔗 Analysis chain

Verify configuration file references

The documentation refers to next-i18next.config.js, but earlier sections mention next-i18next-static-site.config.js. This inconsistency needs to be addressed.

Let's verify the correct configuration file name:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check which configuration file exists in the repository
fd -e js -E 'node_modules' 'next-i18next.*config'

Length of output: 72


Script:

#!/bin/bash
# Search for any mentions of the static site config file
rg "next-i18next-static-site\.config"

Length of output: 159

🧰 Tools
🪛 Markdownlint (0.37.0)

255-255: Expected: 0; Actual: 2
Unordered list indentation

(MD007, ul-indent)


256-256: Expected: 0; Actual: 2
Unordered list indentation

(MD007, ul-indent)


260-260: Expected: 0; Actual: 2
Unordered list indentation

(MD007, ul-indent)

namespaces: ["landing-page", "common", "tools"],
defaultNamespace: "landing-page",
},
};
```
- Add new locale routing.

`lib/i18nPaths.js`
`utils/i18n.ts`
```diff
const i18nPaths = {
en: [
english: [
"/tools/cli"
],
de: [
deutsch: [
"/tools/cli"
],
+ fr: [
+ french: [
+ "/tools/cli"
+ ]
};
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 Icons for asyncapi website
*/
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>
);
}
35 changes: 21 additions & 14 deletions components/languageSelector/LanguageSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { twMerge } from 'tailwind-merge';

import type { SelectProps } from '../form/Select';
import IconLanguage from '../icons/Language';

/**
* @description LanguageSelect component for selecting a language.
Expand All @@ -12,19 +13,25 @@ import type { SelectProps } from '../form/Select';
*/
export default function LanguageSelect({ className = '', onChange = () => {}, options = [], selected }: SelectProps) {
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'>
{option.text}
</option>
))}
</select>
</div>
</div>
);
}
36 changes: 34 additions & 2 deletions components/navigation/MobileNavMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Link from 'next/link';
import React, { useState } from 'react';

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 +20,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 Down Expand Up @@ -104,7 +113,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 +136,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'
>
{lang.text}
</button>
))}
</div>
</div>
</div>
Comment on lines +139 to +161
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve accessibility of the language selection menu.

The current implementation has two potential accessibility issues:

  1. Using a div with onClick instead of a button for the language menu trigger
  2. Missing ARIA attributes for the expandable menu

Consider this improvement:

-          <div className='space-y-2 px-5 py-2' onClick={() => showMenu('language')}>
+          <div className='space-y-2 px-5 py-2'>
+            <button
+              onClick={() => showMenu('language')}
+              aria-expanded={open === 'language'}
+              aria-controls="language-menu"
+              className="w-full text-left"
+            >
               <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' &&
+                  <div
+                    id="language-menu"
+                    role="menu"
+                    className={open === 'language' ? 'block' : 'hidden'}
+                  >
                     uniqueLangs.map((lang) => (
                       <button
                         key={lang.key}
                         onClick={() => changeLanguage(lang.value.toLowerCase(), true)}
+                        role="menuitem"
                         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'
                       >
                         {lang.text}
                       </button>
-                    ))}
+                    )}
+                  </div>
                 </div>
               </div>
+            </button>
           </div>

Committable suggestion skipped: line range outside the PR's diff.

</div>
</div>
</div>
Expand Down
Loading
Loading