Skip to content

Commit

Permalink
DOP-4457: Strip away locale code from slugs before localizing them (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
rayangler authored Mar 14, 2024
1 parent e96c974 commit 1576426
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 22 deletions.
2 changes: 1 addition & 1 deletion src/components/SEO.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const SEO = ({ pageTitle, siteTitle, showDocsLandingTitle, canonical, slug }) =>
const hrefLangLinks = Object.entries(localeHrefMap).map(([langCode, href]) => {
const hrefLang = langCode === 'en-us' ? 'x-default' : langCode;
// Do not remove class. This is used to prevent Smartling from potentially overwriting these links
const smartlingNoRewriteClass = 'sl_norewrite';
const smartlingNoRewriteClass = 'sl_opaque';
return <link key={hrefLang} className={smartlingNoRewriteClass} rel="alternate" hrefLang={hrefLang} href={href} />;
});

Expand Down
37 changes: 34 additions & 3 deletions src/utils/locale.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { withPrefix } from 'gatsby';
import { assertTrailingSlash } from './assert-trailing-slash';
import { isBrowser } from './is-browser';
import { normalizePath } from './normalize-path';
import { removeLeadingSlash } from './remove-leading-slash';

// Update this as more languages are introduced
export const AVAILABLE_LANGUAGES = [
Expand All @@ -13,6 +14,35 @@ export const AVAILABLE_LANGUAGES = [

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

/**
* Strips the first locale code found in the slug. This function should be used to determine the original un-localized path of a page.
* This assumes that the locale code is the first part of the URL slug. For example: "/zh-cn/docs/foo".
* @param {string} slug
* @returns {string}
*/
const stripLocale = (slug) => {
// Smartling has extensive replace logic for URLs and slugs that follow the pattern of "https://www.mongodb.com/docs". However,
// there are instances where we can't rely on them for certain components
if (!slug) {
return '';
}

// Normalize the slug in case any malformed slugs appear like: "//zh-cn/docs"
const slugWithSlash = slug.startsWith('/') ? slug : `/${slug}`;
const normalizedSlug = normalizePath(slugWithSlash);
const firstPathSlug = normalizedSlug.split('/', 2)[1];

// Replace from the original slug to maintain original form
const res = validateCode(firstPathSlug) ? normalizePath(slug.replace(firstPathSlug, '')) : slug;
if (res.startsWith('/') && !slug.startsWith('/')) {
return removeLeadingSlash(res);
} else if (!res.startsWith('/') && slug.startsWith('/')) {
return `/${res}`;
} else {
return res;
}
};

/**
* Returns the locale code based on the current location pathname of the page.
* @returns {string}
Expand Down Expand Up @@ -50,13 +80,14 @@ export const localizePath = (pathname, localeCode) => {
return '';
}

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

/**
Expand All @@ -74,7 +105,7 @@ export const getLocaleMapping = (siteUrl, slug) => {
AVAILABLE_LANGUAGES.forEach(({ code }) => {
const localizedPath = localizePath(slugForUrl, code);
const targetUrl = normalizedSiteUrl + localizedPath;
localeHrefMap[code] = targetUrl;
localeHrefMap[code] = assertTrailingSlash(targetUrl);
});

return localeHrefMap;
Expand Down
4 changes: 3 additions & 1 deletion src/utils/url-utils.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { generatePrefix } from '../components/VersionDropdown/utils';
import { assertTrailingSlash } from './assert-trailing-slash';
import { DOTCOM_BASE_URL } from './base-url';
import { localizePath } from './locale';

export const getUrl = (branchUrlSlug, project, siteMetadata, siteBasePrefix, slug) => {
if (branchUrlSlug === 'legacy') {
// Avoid trailing slash to ensure query param remains valid
const legacyPath = localizePath(`/docs/legacy/?site=${project}`);
return DOTCOM_BASE_URL + legacyPath;
}
const prefixWithVersion = generatePrefix(branchUrlSlug, siteMetadata, siteBasePrefix);
return localizePath(`${prefixWithVersion}/${slug}`);
return assertTrailingSlash(localizePath(`${prefixWithVersion}/${slug}`));
};
2 changes: 1 addition & 1 deletion tests/unit/Head.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ describe('Head', () => {
it.each([['/'], ['foo']])('renders based on slug', (slug) => {
const mockPageContext = { ...mockHeadPageContext, slug };
const { container } = render(<Head pageContext={mockPageContext} />);
const hrefLangLinks = container.querySelectorAll('link.sl_norewrite');
const hrefLangLinks = container.querySelectorAll('link.sl_opaque');
expect(hrefLangLinks).toHaveLength(AVAILABLE_LANGUAGES.length);
expect(hrefLangLinks).toMatchSnapshot();
});
Expand Down
16 changes: 8 additions & 8 deletions tests/unit/__snapshots__/Head.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@
exports[`Head hreflang links renders based on slug 1`] = `
NodeList [
<link
class="sl_norewrite"
class="sl_opaque"
href="https://www.mongodb.com/"
hreflang="x-default"
rel="alternate"
/>,
<link
class="sl_norewrite"
class="sl_opaque"
href="https://www.mongodb.com/zh-cn/"
hreflang="zh-cn"
rel="alternate"
/>,
<link
class="sl_norewrite"
class="sl_opaque"
href="https://www.mongodb.com/ko-kr/"
hreflang="ko-kr"
rel="alternate"
/>,
<link
class="sl_norewrite"
class="sl_opaque"
href="https://www.mongodb.com/pt-br/"
hreflang="pt-br"
rel="alternate"
Expand All @@ -32,25 +32,25 @@ NodeList [
exports[`Head hreflang links renders based on slug 2`] = `
NodeList [
<link
class="sl_norewrite"
class="sl_opaque"
href="https://www.mongodb.com/foo/"
hreflang="x-default"
rel="alternate"
/>,
<link
class="sl_norewrite"
class="sl_opaque"
href="https://www.mongodb.com/zh-cn/foo/"
hreflang="zh-cn"
rel="alternate"
/>,
<link
class="sl_norewrite"
class="sl_opaque"
href="https://www.mongodb.com/ko-kr/foo/"
hreflang="ko-kr"
rel="alternate"
/>,
<link
class="sl_norewrite"
class="sl_opaque"
href="https://www.mongodb.com/pt-br/foo/"
hreflang="pt-br"
rel="alternate"
Expand Down
28 changes: 20 additions & 8 deletions tests/unit/utils/locale.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ describe('localizePath', () => {

it.each([
['/', '/zh-cn/'],
['/page/slug', '/zh-cn/page/slug/'],
['page/slug', 'zh-cn/page/slug/'],
['/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(() => ({
Expand All @@ -40,28 +40,40 @@ describe('localizePath', () => {

it.each([
['/', 'ko-kr', '/ko-kr/'],
['/page/slug', 'pt-br', '/pt-br/page/slug/'],
['page/slug', 'zh-cn', 'zh-cn/page/slug/'],
['/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/'],
['/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/'],
['/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);
});

it.each([
['zh-cn/docs', 'zh-cn/docs'],
['/ko-kr/docs/foo', '/zh-cn/docs/foo'],
['/pt-br/', '/zh-cn/'],
['//ko-kr/', '/zh-cn/'],
['pt-br/', 'zh-cn/'],
['pt-br/docs/pt-br', 'zh-cn/docs/pt-br'],
])('handles slugs that already have a locale code', (slug, expectedRes) => {
const res = localizePath(slug, 'zh-cn');
expect(res).toEqual(expectedRes);
});
});

0 comments on commit 1576426

Please sign in to comment.