Skip to content
This repository has been archived by the owner on Mar 14, 2024. It is now read-only.

feat(i18n): allow easy navigation in /i18n/ directory, fixes #6669 #7283

Closed
wants to merge 4 commits into from
Closed
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
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
.tmp/*
dist/*
types/*
/functions/supportedLocales.js
/src/site/content/en/patterns/**
46 changes: 31 additions & 15 deletions firebase-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,34 @@ const fs = require('fs');

const redirectsYaml = fs.readFileSync('./redirects.yaml', 'utf8');
const {redirects: parsedRedirects} = yaml.safeLoad(redirectsYaml);

const {defaultLocale, supportedLocales} = require('./shared/locale.js');
const firebaseJson = require('./firebase.incl.json');
firebaseJson.hosting.redirects = parsedRedirects.reduce(
(redirects, redirect) => {
const type = [301, 302].includes(redirect.type) ? redirect.type : 301;
if (redirect.source && redirect.destination) {
redirects.push({
source: redirect.source,
destination: redirect.destination,
type,
});
}
return redirects;
},
[],
);

const redirects = [];

// YAML defined redirects
for (const redirect of parsedRedirects) {
const type = [301, 302].includes(redirect.type) ? redirect.type : 301;
if (redirect.source && redirect.destination) {
redirects.push({
source: redirect.source,
destination: redirect.destination,
type,
});
}
}

// i18n redirects
for (const locale of supportedLocales) {
firebaseJson.hosting.redirects.push({
source: `/${locale}/:path*`,
destination: locale === defaultLocale ? `/:path` : `/i18n/${locale}/:path`,
type: 301,
});
}

firebaseJson.hosting.redirects = redirects;

if (process.env.ELEVENTY_ENV === 'prod') {
const hashListJson = fs.readFileSync('dist/script-hash-list.json', 'utf-8');
const hashList = JSON.parse(hashListJson);
Expand All @@ -37,3 +49,7 @@ if (process.env.ELEVENTY_ENV === 'prod') {
}

fs.writeFileSync('./firebase.json', JSON.stringify(firebaseJson, null, 2));
fs.writeFileSync(
'./functions/supportedLocales.js',
`export default ${JSON.stringify(supportedLocales)};`,
);
8 changes: 7 additions & 1 deletion firebase.incl.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@
"i18n": {
"root": "/i18n"
},
"redirects": []
"redirects": [],
"rewrites": [
{
"source": "/i18n/**",
"function": "i18n404"
}
]
},
"emulators": {
"hosting": {
Expand Down
3 changes: 2 additions & 1 deletion functions/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules/
node_modules/
supportedLocales.js
15 changes: 15 additions & 0 deletions functions/https/i18n-404.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as functions from 'firebase-functions';
// eslint-disable-next-line node/no-missing-import
import supportedLocales from '../supportedLocales.js';

export const i18n404 = functions.https.onRequest((request, response) => {
const path = request.path.split('/');

// Redirect for locales
if (supportedLocales.includes(path[2])) {
response.redirect(`/${path.splice(3).join('/')}`, 301);
} else {
// Redirect for everything else
response.redirect(`/${path.splice(2).join('/')}`, 301);
}
});
1 change: 1 addition & 0 deletions functions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ import admin from 'firebase-admin';

admin.initializeApp();

export * from './https/i18n-404.js';
export * from './pubsub/scheduled-firestore-export.js';
export * from './pubsub/youtube.js';
1 change: 1 addition & 0 deletions src/lib/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import {store} from './store';
import {localStorage} from './utils/storage';
import removeServiceWorkers from './utils/sw-remove';
import './i18n';

// This hides a legacy browser warning that can appear on the /measure page
// See .unsupported-notice in _page-header.scss
Expand Down
26 changes: 26 additions & 0 deletions src/lib/i18n.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Detects if link should navigate to `i18n` path
* and then navigates there.
*
* @param {MouseEvent} e
*/
function i18nClickEvent(e) {
let target = /** @type {HTMLElement} */ (e.target);

if (target && target.tagName !== 'A') {
target = /** @type {HTMLElement} */ (target?.parentNode);
}

const href = target.getAttribute('href');

if (
window.location.pathname.startsWith('/i18n') &&
target.tagName === 'A' &&
!href.startsWith('#')
) {
const urlRegex = window.location.pathname.match(/\/i18n\/([a-z]{2})\/*/);
target.setAttribute('href', `/i18n/${urlRegex[1]}${href}`);
}
}

document.addEventListener('click', i18nClickEvent);