Shopify offers a nice and easy way to create menu for your app via extensions.
One issue though, it is not possible to translate it.
In this post, learn how to create your own multilingual custom navigation links for your app!
Before starting, install the needed packages.
Via command line:
npm install --save @shopify/polaris i18next react-i18next
Now we need to set up i18next
and react-i18next
so that we can translate our navigation links (and the whole application).
In your index.js
file, import i18next
and initReactI18next
:
import i18next from 'i18next';
import { initReactI18next } from 'react-i18next';
import fr from './locales/fr.json'; // Our translation file.
Initialize i18next
with your configuration then render your app:
i18next
// Bind react-i18next to the instance.
// Will be useful in other components to get the current language instance.
.use(initReactI18next)
// Initialize i18next as you wish, just demo below.
.init({
debug: false,
resources: {
fr: fr
},
lng: 'en',
fallbackLng: 'en',
interpolation: {
escapeValue: false
},
keySeparator: false,
nsSeparator: false
})
.then(() => {
ReactDOM.render(
<React.StrictMode>
<Navigation/>
</React.StrictMode>,
document.getElementById('root')
);
});
You probably noticed from the i18next
initialization, we render a component called Navigation
.
This component will be in charge of displaying our custom multilingual navigation links.
What the magic trick ? Nothing special really, a simple Polaris Tabs
component.
Here is the list of needed imports
:
import React, {useState, useCallback} from 'react';
import {
AppProvider,
Frame,
Page,
Tabs,
FooterHelp,
Link,
Button,
Popover,
ActionList
} from '@shopify/polaris';
import enTranslations from '@shopify/polaris/locales/en.json';
import frTranslations from '@shopify/polaris/locales/fr.json';
import {useTranslation} from 'react-i18next';
import Configuration from './pages/Configuration';
import Customers from './pages/Customers';
import Sales from './pages/Sales';
And here the first part of our component:
function Navigation() {
// t() will be in charge of translations
// i18n() will handle language changes
const [t, i18n] = useTranslation();
// Our selected tab.
const [selected, setSelected] = useState(0);
const handleTabChange = useCallback(
(selectedTabIndex) => setSelected(selectedTabIndex),
[],
);
// Handle the language selector popover.
const [active, setActive] = useState(false);
const toggleActive = useCallback(
() => setActive((active) => !active),
[]
);
// Shopify components translations.
// Will be used in the AppProvider component.
const polarisTranslation = {
fr: frTranslations,
en: enTranslations
}
// Our navigation links.
// See that \"content\" is translated thanks to t() function.
const tabs = [
{
id: 'configuration',
content: t('Configuration'),
panelID: 'configuration-content'
},
{
id: 'customers',
content: t('Customers'),
panelID: 'customers-content'
},
{
id: 'sales',
content: t('Sales'),
panelID: 'sales-content'
}
];
const activator = (
<Button onClick={toggleActive} disclosure plain>
{t('Change the current language')}
</Button>
);
// Our components, indexed the same as our tabs.
const pages = [
<Configuration/>,
<Customers/>,
<Sales/>
];
// Wrap our tabs in an AppProvider with the current language translation
// and a Frame.
// In recent version of Polaris, you'll also need to use a Provider
// component, not used here for the sake of simplicity.
// Then the Tabs component will handle your different navigation links
// and their corresponding components, here wrapped in a Page
// component.
//
// For this example, we add a FooterHelp where you can change the
// language directly to see it in action.
// In real life application you'll simply need to use the browser
// language and create a form to let merchants select their preferred
// language.
return (
<AppProvider i18n={polarisTranslation[i18n.language]}>
<Frame>
<Tabs tabs={tabs} selected={selected} onSelect={handleTabChange}>
<Page fullWidth>
{pages[selected]}
</Page>
</Tabs>
<FooterHelp>
<div style={{textAlign: 'center'}}>
<Popover active={active} activator={activator} onClose={toggleActive}>
<ActionList
items={[
{
content: t('English'),
onAction: () => i18n.changeLanguage('en'),
disabled: i18n.language === 'en'
},
{
content: t('French'),
onAction: () => i18n.changeLanguage('fr'),
disabled: i18n.language === 'fr'
},
]}
/>
</Popover>
</div>
</FooterHelp>
</Frame>
</AppProvider>
);
}
export default Navigation;
A demonstration of this code in action is available here (hosted on Netlify).
As you can see, it is really not that hard to implement your own navigation links and translate them.
An other way of doing it would be to use a Stack
component instead, with some CSS, so that you could also add buttons in the right side of navigation links (like when you add primaryAction
and/or secondaryActions
to a Page
component and it adds the corresponding action as buttons in the navigation link bar).
It might be a good subject for an other blog post.
If you want to try it on your own or copy the code for your own application, the source code is available on Github.