Skip to content

Commit

Permalink
DOP-4453: Include locale when using version selector (#1037)
Browse files Browse the repository at this point in the history
  • Loading branch information
rayangler committed Mar 13, 2024
1 parent 897f2f8 commit aaad9ae
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 23 deletions.
56 changes: 53 additions & 3 deletions src/utils/locale.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { withPrefix } from 'gatsby';
import { assertTrailingSlash } from './assert-trailing-slash';
import { isBrowser } from './is-browser';
import { normalizePath } from './normalize-path';

// Update this as more languages are introduced
export const AVAILABLE_LANGUAGES = [
Expand All @@ -9,6 +11,54 @@ export const AVAILABLE_LANGUAGES = [
{ language: 'Português', code: 'pt-br' },
];

const validateCode = (potentialCode) => !!AVAILABLE_LANGUAGES.find(({ code }) => potentialCode === code);

/**
* Returns the locale code based on the current location pathname of the page.
* @returns {string}
*/
const getCurrLocale = () => {
const defaultLang = 'en-us';

if (!isBrowser) {
return defaultLang;
}

// This currently needs to be client-side because the source page doesn't know about locale at
// build time. Smartling's GDN handles localization
// Example on https://www.mongodb.com/zh-cn/docs/manual/introduction:
// expected pathname - /zh-cn/docs/manual/introduction; expected locale - "zh-cn"
const pathname = window.location.pathname;
const expectedDocsPrefixes = ['docs', 'docs-qa'];
const firstPathSlug = pathname.split('/', 2)[1];
if (expectedDocsPrefixes.includes(firstPathSlug)) {
return defaultLang;
}

const slugMatchesCode = validateCode(firstPathSlug);
return slugMatchesCode ? firstPathSlug : defaultLang;
};

/**
* Returns the pathname with its locale code prepended. Leading slashes are preserved, if they exist.
* @param {string} pathname - Path name or slug of the page
* @param {string?} localeCode - Optional locale code. By default, the code is determined based on the current location of the page
* @returns {string}
*/
export const localizePath = (pathname, localeCode) => {
if (!pathname) {
return '';
}

const code = localeCode && validateCode(localeCode) ? localeCode : getCurrLocale();
const languagePrefix = code === 'en-us' ? '' : `${code}/`;
let newPath = languagePrefix + pathname;
if (pathname.startsWith('/')) {
newPath = `/${newPath}`;
}
return assertTrailingSlash(normalizePath(newPath));
};

/**
* Returns a mapping of a page's URL and its equivalent URLs for different languages.
* @param {string} siteUrl
Expand All @@ -22,9 +72,9 @@ export const getLocaleMapping = (siteUrl, slug) => {
const localeHrefMap = {};

AVAILABLE_LANGUAGES.forEach(({ code }) => {
const langPrefix = code === 'en-us' ? '' : `/${code}`;
const targetUrl = `${normalizedSiteUrl}${langPrefix}${slugForUrl}`;
localeHrefMap[code] = assertTrailingSlash(targetUrl);
const localizedPath = localizePath(slugForUrl, code);
const targetUrl = normalizedSiteUrl + localizedPath;
localeHrefMap[code] = targetUrl;
});

return localeHrefMap;
Expand Down
10 changes: 5 additions & 5 deletions src/utils/url-utils.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { generatePrefix } from '../components/VersionDropdown/utils';
import { baseUrl } from './base-url';
import { assertTrailingSlash } from './assert-trailing-slash';
import { normalizePath } from './normalize-path';
import { DOTCOM_BASE_URL } from './base-url';
import { localizePath } from './locale';

export const getUrl = (branchUrlSlug, project, siteMetadata, siteBasePrefix, slug) => {
if (branchUrlSlug === 'legacy') {
return `${baseUrl()}legacy/?site=${project}`;
const legacyPath = localizePath(`/docs/legacy/?site=${project}`);
return DOTCOM_BASE_URL + legacyPath;
}
const prefixWithVersion = generatePrefix(branchUrlSlug, siteMetadata, siteBasePrefix);
return assertTrailingSlash(normalizePath(`${prefixWithVersion}/${slug}`));
return localizePath(`${prefixWithVersion}/${slug}`);
};
39 changes: 37 additions & 2 deletions tests/unit/VersionDropdown.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ const fetchDocset = () => {
aliases: null,
gitBranchName: 'v4.11',
urlSlug: null,
urlAliases: ['v.411'],
urlAliases: ['v4.11'],
isStableBranch: true,
},
{
Expand Down Expand Up @@ -291,7 +291,42 @@ describe('VersionDropdown', () => {
await tick();

expect(navigate).toBeCalled();
expect(navigate).toBeCalledWith('/docs-test/drivers/node/v.411/');
expect(navigate).toBeCalledWith('/docs-test/drivers/node/v4.11/');
});

it('calls the navigate function for translated pages', async () => {
// Pretend page exists on Korean-translated site
global.window = Object.create(window);
Object.defineProperty(window, 'location', {
value: {
pathname: '/ko-kr/docs-test/drivers/node/current',
},
});

await act(async () => {
mountConsumer();
});

const versionDropdown = screen.queryByRole('button', { name: 'master' });
expect(versionDropdown).toBeInTheDocument();

await act(async () => {
userEvent.click(versionDropdown);
});
await tick();

const versionOptionsDropdown = queryElementWithin(versionDropdown, 'listbox');
const versionOptions = within(versionOptionsDropdown).queryAllByRole('option');
expect(versionOptions.length).toBe(3);

await act(async () => {
userEvent.click(versionOptions[1], undefined, {
skipPointerEventsCheck: true,
});
});

await tick();
expect(navigate).toBeCalledWith('/ko-kr/docs-test/drivers/node/v4.11/');
});
});
});
6 changes: 3 additions & 3 deletions tests/unit/utils/__snapshots__/locale.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`returns a valid mapping of URLs 1`] = `
exports[`getLocaleMapping returns a valid mapping of URLs 1`] = `
{
"en-us": "https://www.mongodb.com/docs/",
"ko-kr": "https://www.mongodb.com/docs/ko-kr/",
Expand All @@ -9,7 +9,7 @@ exports[`returns a valid mapping of URLs 1`] = `
}
`;

exports[`returns a valid mapping of URLs 2`] = `
exports[`getLocaleMapping returns a valid mapping of URLs 2`] = `
{
"en-us": "https://www.mongodb.com/docs/about/page/",
"ko-kr": "https://www.mongodb.com/docs/ko-kr/about/page/",
Expand All @@ -18,7 +18,7 @@ exports[`returns a valid mapping of URLs 2`] = `
}
`;

exports[`returns a valid mapping of URLs 3`] = `
exports[`getLocaleMapping returns a valid mapping of URLs 3`] = `
{
"en-us": "https://www.mongodb.com/introduction/",
"ko-kr": "https://www.mongodb.com/ko-kr/introduction/",
Expand Down
76 changes: 66 additions & 10 deletions tests/unit/utils/locale.test.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,67 @@
import { AVAILABLE_LANGUAGES, getLocaleMapping } from '../../../src/utils/locale';

it.each([
['https://www.mongodb.com/docs/', '/'],
['https://www.mongodb.com/docs', '/about/page/'],
['https://www.mongodb.com', 'introduction'],
])('returns a valid mapping of URLs', (siteUrl, slug) => {
const mapping = getLocaleMapping(siteUrl, slug);
expect(Object.keys(mapping)).toHaveLength(AVAILABLE_LANGUAGES.length);
expect(mapping).toMatchSnapshot();
import { AVAILABLE_LANGUAGES, getLocaleMapping, localizePath } from '../../../src/utils/locale';

describe('getLocaleMapping', () => {
it.each([
['https://www.mongodb.com/docs/', '/'],
['https://www.mongodb.com/docs', '/about/page/'],
['https://www.mongodb.com', 'introduction'],
])('returns a valid mapping of URLs', (siteUrl, slug) => {
const mapping = getLocaleMapping(siteUrl, slug);
expect(Object.keys(mapping)).toHaveLength(AVAILABLE_LANGUAGES.length);
expect(mapping).toMatchSnapshot();
});
});

describe('localizePath', () => {
let windowSpy;

beforeEach(() => {
windowSpy = jest.spyOn(window, 'window', 'get');
});

afterEach(() => {
windowSpy.mockRestore();
});

it.each([
['/', '/zh-cn/'],
['/page/slug', '/zh-cn/page/slug/'],
['page/slug', 'zh-cn/page/slug/'],
])('returns localized path when no code is set', (slug, expectedRes) => {
// Pretend page exists on translated site
windowSpy.mockImplementation(() => ({
location: {
pathname: '/zh-cn/docs-test/drivers/node/current',
},
}));
const res = localizePath(slug);
expect(res).toEqual(expectedRes);
});

it.each([
['/', 'ko-kr', '/ko-kr/'],
['/page/slug', 'pt-br', '/pt-br/page/slug/'],
['page/slug', 'zh-cn', 'zh-cn/page/slug/'],
])('returns localized path when code is set', (slug, code, expectedRes) => {
const res = localizePath(slug, code);
expect(res).toEqual(expectedRes);
});

it.each([
['/', '/'],
['/page/slug', '/page/slug/'],
['page/slug', 'page/slug/'],
])('returns the same page slug when English is found by default', (slug, expectedRes) => {
const res = localizePath(slug);
expect(res).toEqual(expectedRes);
});

it.each([
['/', '/'],
['/page/slug', '/page/slug/'],
['page/slug', 'page/slug/'],
])('gracefully defaults to English path when invalid language is passed in', (slug, expectedRes) => {
const res = localizePath(slug, 'beep-boop');
expect(res).toEqual(expectedRes);
});
});

0 comments on commit aaad9ae

Please sign in to comment.