Skip to content

Commit 02805f6

Browse files
author
Marcin Kwiatkowski
committed
feat(middleware): added i18n route middleware (#408)
* feat(middleware): added i18n route middleware closes #378 * refactor(middleware): i18n middleware: adjustements
1 parent adf3ca2 commit 02805f6

File tree

9 files changed

+213
-3
lines changed

9 files changed

+213
-3
lines changed
224 KB
Loading

docs/middlewares/index.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Middlewares
2+
3+
Here you can find documentation for global Middlewares
4+
5+
## i18n
6+
7+
i18n middleware is responsible for handling store changes after URL changes.
8+
9+
It checks if locale in i18n module is changes and update `vsf-store` and `vsf-locale` cookie to match new store on both
10+
client and server side.
11+
12+
### Flow
13+
1. Start: load middleware
14+
2. Read current Magento Store Code from vsf-store cookie
15+
3. Is store code found?
16+
2. No: Set default locale if isn't already set
17+
1. End
18+
4. Yes: Check if current store code has corresponding locale in Nuxt.config.js
19+
1. No: Set default locale if isn't already set
20+
1. End
21+
5. Yes: If corresponded locale is different than current locale
22+
1. No: End
23+
6. Yes: set new locale
24+
7. Go forward (load the page with correct locale and date)
25+
8. End
26+
27+
28+
![i18n flow](./i18n-middleware-diagram.png)

packages/composables/src/getters/storeConfigGetters.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { StoreConfig } from '@vue-storefront/magento-api';
2-
import { isoToCode } from '../helpers/isoToCode';
1+
import { StoreConfig } from '@vue-storefront/magento-api';;
32

43
const getCode = (config: StoreConfig) => config.store_code;
54
const getTitle = (config: StoreConfig) => config.default_title;

packages/composables/src/helpers/isoToCode.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import i18nMiddleware from '~/middleware/i18n';
2+
3+
const localesMock = [
4+
{
5+
code: 'default',
6+
file: 'en.js',
7+
iso: 'en_US',
8+
},
9+
{
10+
code: 'de_DE',
11+
file: 'de.js',
12+
iso: 'de_DE',
13+
},
14+
{
15+
code: 'nl_NL',
16+
file: 'en.js',
17+
iso: 'en_US',
18+
},
19+
];
20+
21+
const apiStateMock = {
22+
getCurrency: () => 'USD',
23+
getCountry: () => 'PL',
24+
getCartId: () => '123',
25+
getCustomerToken: () => '12fg45',
26+
};
27+
28+
const appMock = {
29+
$cookies: {
30+
get: jest.fn(),
31+
},
32+
i18n: {
33+
defaultLocale: 'en',
34+
setLocale: jest.fn(),
35+
locales: localesMock,
36+
},
37+
$vsf: {
38+
$magento: {
39+
config: {
40+
state: {
41+
...apiStateMock,
42+
setStore: jest.fn(),
43+
setLocale: jest.fn(),
44+
},
45+
axios: {
46+
headers: {
47+
cookie: null,
48+
},
49+
},
50+
},
51+
},
52+
},
53+
};
54+
55+
describe('i18n middleware', () => {
56+
beforeEach(() => {
57+
jest.resetAllMocks();
58+
});
59+
60+
it('Should read vsf-store cookie value', () => {
61+
i18nMiddleware({ app: appMock });
62+
63+
expect(appMock.$cookies.get).toHaveBeenCalledWith('vsf-store');
64+
});
65+
66+
it('Should find locale based on magento store code', () => {
67+
appMock.$cookies.get.mockReturnValue('default');
68+
i18nMiddleware({ app: appMock });
69+
70+
expect(appMock.i18n.setLocale).not.toHaveBeenCalled();
71+
});
72+
73+
it('Should set default locale when there is no locale that match given magento store code', () => {
74+
appMock.$cookies.get.mockReturnValue('pl_PL');
75+
76+
expect(appMock.i18n.setLocale).not.toHaveBeenCalledTimes(1);
77+
});
78+
79+
it('Should set default locale when vsf-store cookie is not exist', () => {
80+
appMock.$cookies.get.mockReturnValue(null);
81+
i18nMiddleware({ app: appMock });
82+
83+
expect(appMock.i18n.setLocale).toHaveBeenCalledWith('en');
84+
});
85+
86+
it('Should change locale if a new selected store has a different locale', () => {
87+
const testCaseAppMock = {
88+
...appMock,
89+
i18n: {
90+
...appMock.i18n,
91+
locale: 'de_DE',
92+
},
93+
};
94+
95+
testCaseAppMock.$cookies.get.mockReturnValueOnce('de_DE').mockReturnValueOnce('default');
96+
97+
i18nMiddleware({ app: testCaseAppMock });
98+
99+
expect(testCaseAppMock.$vsf.$magento.config.state.setLocale).toHaveBeenCalledWith('de_DE');
100+
expect(testCaseAppMock.$vsf.$magento.config.state.setStore).toHaveBeenCalledWith('de_DE');
101+
expect(testCaseAppMock.$vsf.$magento.config.axios.headers.cookie).toMatchInlineSnapshot(
102+
`"vsf-store=de_DE; vsf-locale=de_DE; vsf-currency=USD; vsf-country=PL; vsf-customer=12fg45; vsf-cart=123 "`
103+
);
104+
});
105+
});

packages/theme/middleware/i18n.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import cookieNames from '~/enums/cookieNameEnum';
2+
3+
/**
4+
* Read vsf-store cookie value.
5+
*
6+
* @param app {NuxtAppOptions} - Nuxt App options object
7+
* @returns {string} - vsf-store cookie value
8+
*/
9+
const readStoreCookie = (app) => app.$cookies.get(cookieNames.storeCookieName);
10+
11+
/**
12+
* Find locale code based on magento store code
13+
* @param storeCode {string} - magento store code
14+
* @param locales {array} - array with locales
15+
* @returns boolean
16+
*/
17+
const findLocaleBasedOnStoreCode = (storeCode, locales) => locales.find((locale) => locale.code === storeCode);
18+
19+
/**
20+
* Set default locale
21+
* @param i18n {i18n} i18n API
22+
* @returns {Promise<void>}
23+
*/
24+
const setDefaultLocale = async (i18n) => {
25+
await i18n.setLocale(i18n.defaultLocale);
26+
};
27+
28+
/**
29+
* Prepare new cookie string based on app state.
30+
*
31+
* @param apiState {ConfigState}
32+
* @returns {string}
33+
*/
34+
const prepareNewCookieString = (apiState, newStoreCode) => {
35+
let cookie = `vsf-store=${newStoreCode}; `;
36+
37+
cookie += `vsf-locale=${newStoreCode}; `;
38+
cookie += `vsf-currency=${apiState.getCurrency()}; `;
39+
cookie += `vsf-country=${apiState.getCountry()}; `;
40+
41+
const customerTokenCookie = apiState.getCustomerToken();
42+
43+
if (customerTokenCookie) {
44+
cookie += `vsf-customer=${customerTokenCookie}; `;
45+
}
46+
47+
const cartIdCookie = apiState.getCartId();
48+
49+
if (cartIdCookie) {
50+
cookie += `vsf-cart=${cartIdCookie} `;
51+
}
52+
53+
return cookie;
54+
};
55+
56+
export default async ({ app }) => {
57+
const { i18n } = app;
58+
const currentStoreCode = readStoreCookie(app);
59+
60+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
61+
if (!currentStoreCode || !findLocaleBasedOnStoreCode(currentStoreCode, i18n.locales)) {
62+
await setDefaultLocale(i18n);
63+
64+
return;
65+
}
66+
67+
const i18nCurrentLocaleCode = i18n.locale;
68+
const localeCookie = app.$cookies.get(cookieNames.localeCookieName);
69+
70+
if (i18nCurrentLocaleCode !== localeCookie) {
71+
const apiState = app.$vsf.$magento.config.state;
72+
73+
apiState.setStore(i18nCurrentLocaleCode);
74+
apiState.setLocale(i18nCurrentLocaleCode);
75+
76+
// eslint-disable-next-line no-param-reassign
77+
app.$vsf.$magento.config.axios.headers.cookie = prepareNewCookieString(apiState, i18nCurrentLocaleCode);
78+
}
79+
};

0 commit comments

Comments
 (0)