Skip to content
This repository was archived by the owner on May 10, 2021. It is now read-only.

Support for i18n in Next 10 #75

Merged
merged 22 commits into from
Jan 3, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
cc4de30
rough pass at i18n ssg breaking changes in next 10
lindsaylevine Nov 12, 2020
89b22e6
update withoutProps/redirects to accommodate defaultLocale
lindsaylevine Nov 29, 2020
7da18c2
very dirty Just Working i18n for most cases
lindsaylevine Dec 11, 2020
b475f4d
pre clean-up tests, no cypress yet
lindsaylevine Dec 11, 2020
c68d361
make i18n getDataRoute helper more DRY
lindsaylevine Dec 11, 2020
0c67c4b
fix windows test
lindsaylevine Dec 11, 2020
0498d83
add documentation for withoutProps and SSR pages logic for i18n
lindsaylevine Dec 12, 2020
c86eb09
clean up getStaticProps setup logic
lindsaylevine Dec 12, 2020
348705d
fix getStaticProps redirects logic to be consistent with setup and he…
lindsaylevine Dec 12, 2020
d45f240
fix revalidate redirects logic and heavily comment
lindsaylevine Dec 12, 2020
9609739
remove superfluous getDataRouteForI18nRoute helper
lindsaylevine Dec 12, 2020
fb11d14
remove superfluous getFilePathForRouteWithI18n helper
lindsaylevine Dec 12, 2020
90856ad
fix existing cypress tests except a few v odd cases
lindsaylevine Dec 16, 2020
acc13c9
fix/move fallback i18n logic
lindsaylevine Dec 17, 2020
db05c51
fix previously commented out failing dataRoute tests
lindsaylevine Dec 19, 2020
2d5e867
fix 404 cypress tests which expect root level 404.html
lindsaylevine Dec 19, 2020
04d57b4
specifically test i18n/non-default locales in cypress
lindsaylevine Dec 20, 2020
60e8a49
root level index pages need special dataRoute logic
lindsaylevine Dec 20, 2020
0d45036
use splats for dynamic ssg i18n dataRoutes
lindsaylevine Dec 20, 2020
96959bd
plain static routes need 2 redirects: one to defaultLocale, one to pr…
lindsaylevine Dec 20, 2020
447426d
massive cleanup/refactor to transform manifest
lindsaylevine Dec 22, 2020
25ff5fe
missing initialProps + fallback logic + PR feedback
lindsaylevine Jan 3, 2021
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
29 changes: 29 additions & 0 deletions lib/helpers/getDefaultLocaleRedirectForI18n.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const i18n = require("./getI18n")();
const { defaultLocale } = i18n;

// In i18n projects, we need to create redirects from the "naked" route
// to the defaultLocale-prepended route i.e. /static -> /en/static
// Note: there can only one defaultLocale, but we put it in an array to simplify
// logic in redirects.js files via concatenation
const getDefaultLocaleRedirectForI18n = (route, srcRoute, target) => {
// If no i18n, skip
if (!defaultLocale) return [];

const routePieces = route.split("/");
const routeLocale = routePieces[1];
if (routeLocale === defaultLocale) {
const nakedRoute =
route === `/${routeLocale}` ? "/" : route.replace(`/${routeLocale}`, "");
// const nakedRoute = routePieces.slice(2, routePieces.length).join("/");
return [
{
route: nakedRoute,
target: target || route,
},
];
}

return [];
};

module.exports = getDefaultLocaleRedirectForI18n;
10 changes: 10 additions & 0 deletions lib/helpers/getI18n.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Get the i1i8n details specified in next.config.js, if any
const getNextConfig = require("./getNextConfig");

const getI18n = () => {
const nextConfig = getNextConfig();

return nextConfig.i18n || { locales: [] };
};

module.exports = getI18n;
24 changes: 24 additions & 0 deletions lib/helpers/getLocaleRoutesForI18n.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const i18n = require("./getI18n")();
const getDataRouteForRoute = require("./getDataRouteForRoute");

const getI18nDataRoute = (route, locale) => {
return route === "/"
? getDataRouteForRoute(`/${locale}`)
: getDataRouteForRoute(route, locale);
};

// In i18n projects, Next does not prepend static routes with locales
// so we have to do it manually
const getLocaleRoutesForI18n = ({ route, srcRoute }) => {
// If no i18n or is dynamic route, skip
if (!i18n.defaultLocale || !!srcRoute) return [];
return i18n.locales.map((locale) => {
return {
route: `/${locale}${route}`,
dataRoute: getI18nDataRoute(route, locale),
nakedRoute: route,
};
});
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rn this is only used by gsp/pages.js, might be able to do without this or rename it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nvm its not used anywhere LuL


module.exports = getLocaleRoutesForI18n;
39 changes: 38 additions & 1 deletion lib/helpers/getPrerenderManifest.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,46 @@
const { join } = require("path");
const { readJSONSync } = require("fs-extra");
const { NEXT_DIST_DIR } = require("../config");
const nextConfig = require("./getNextConfig")();
const getDataRouteForRoute = require("./getDataRouteForRoute");

const transformManifestForI18n = (manifest) => {
const { routes, dynamicRoutes } = manifest;
const { defaultLocale, locales } = nextConfig.i18n;
const newRoutes = {};
Object.entries(routes).forEach(
([route, { dataRoute, initialRevalidateSeconds, srcRoute }]) => {
const isDynamicRoute = !!srcRoute;
if (isDynamicRoute) {
newRoutes[route] = routes[route];
} else {
locales.forEach((locale) => {
const routeWithPrependedLocale = `/${locale}${route}`;
newRoutes[routeWithPrependedLocale] = {
dataRoute: getDataRouteForRoute(route, locale),
srcRoute: route,
initialRevalidateSeconds: false,
};
});
}
}
);
const newDynamicRoutes = {};
Object.entries(dynamicRoutes).forEach(([route, { dataRoute, fallback }]) => {
newDynamicRoutes[route] = {
route,
dataRoute: getDataRouteForRoute(route, defaultLocale),
fallback,
};
});

return { ...manifest, routes: newRoutes, dynamicRoutes: newDynamicRoutes };
};

const getPrerenderManifest = () => {
return readJSONSync(join(NEXT_DIST_DIR, "prerender-manifest.json"));
const manifest = readJSONSync(join(NEXT_DIST_DIR, "prerender-manifest.json"));
if (nextConfig.i18n) return transformManifestForI18n(manifest);
return manifest;
};

module.exports = getPrerenderManifest;
12 changes: 12 additions & 0 deletions lib/helpers/isRouteInPrerenderManifest.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
const getPrerenderManifest = require("./getPrerenderManifest");
const i18n = require("./getI18n")();

const { routes, dynamicRoutes } = getPrerenderManifest();
const { defaultLocale, locales } = i18n;

const isRouteInManifestWithI18n = (route) => {
let isStaticRoute = false;
Object.entries(routes).forEach(([staticRoute, { srcRoute }]) => {
// This is because in i18n we set the nakedRoute to be the srcRoute in the manifest
if (route === srcRoute) isStaticRoute = true;
});
return isStaticRoute || route in dynamicRoutes;
};

// Return true if the route is defined in the prerender manifest
const isRouteInPrerenderManifest = (route) => {
if (i18n.defaultLocale) return isRouteInManifestWithI18n(route);
return route in routes || route in dynamicRoutes;
};

Expand Down
4 changes: 2 additions & 2 deletions lib/helpers/isRouteWithDataRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ const getRoutesManifest = require("./getRoutesManifest");
const { dataRoutes } = getRoutesManifest();

// Return true if the route has a data route in the routes manifest
const isRouteInPrerenderManifest = (route) => {
const isRouteWithDataRoute = (route) => {
// If no data routes exist, return false
if (dataRoutes == null) return false;

return dataRoutes.find((dataRoute) => dataRoute.page === route);
};

module.exports = isRouteInPrerenderManifest;
module.exports = isRouteWithDataRoute;
46 changes: 18 additions & 28 deletions lib/pages/getServerSideProps/redirects.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,43 @@
const getNetlifyFunctionName = require("../../helpers/getNetlifyFunctionName");
const getDataRouteForRoute = require("../../helpers/getDataRouteForRoute");
const getLocaleRoutesForI18n = require("../../helpers/getLocaleRoutesForI18n");
const pages = require("./pages");
const nextConfig = require("../../helpers/getNextConfig")();

const redirects = [];

/** getServerSideProps pages
*
* Params:
* route examples -> '/ssr', '/ssr/[id]'
* filePath examples -> 'pages/ssr.js', 'pages/ssr/[id].js'
*
* With i18n enabled:
* route and filePath formats are the same as above
*
* Page params {
* route -> '/ssr', '/ssr/[id]'
* filePath -> 'pages/ssr.js', 'pages/ssr/[id].js'
* }
**/

pages.forEach(({ route, filePath }) => {
const functionName = getNetlifyFunctionName(filePath);
const target = `/.netlify/functions/${functionName}`;

// If i18n, need to add extra redirects for each locale
// i.e. /en/ssr -> /${target}
if (nextConfig.i18n) {
const { locales = [] } = nextConfig.i18n;
locales.forEach((locale) => {
redirects.push({
route: `/${locale}${route}`,
target,
});
redirects.push({
route:
route === "/"
? getDataRouteForRoute(`/${locale}`)
: getDataRouteForRoute(route, locale),
target,
});
// Add any locale redirects if i18n
getLocaleRoutesForI18n({ route }).forEach((localeRoute) => {
redirects.push({
route: localeRoute.route,
target,
});
}
redirects.push({
route: localeRoute.dataRoute,
target,
});
});

// Add one redirect for the default page/route
// Add one redirect for the naked route
// i.e. /ssr
redirects.push({
route,
target,
});

// Add one redirect for the data route
// Next doesn't provide the dataRoute out of the box for us so we
// Add one redirect for the data route;
// pages-manifest doesn't provide the dataRoute for us so we
// construct it ourselves with getDataRouteForRoute
redirects.push({
route: getDataRouteForRoute(route),
Expand Down
3 changes: 2 additions & 1 deletion lib/pages/getStaticProps/pages.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const getPrerenderManifest = require("../../helpers/getPrerenderManifest");
const getLocaleRoutesForI18n = require("../../helpers/getLocaleRoutesForI18n");

// Collect pages
const pages = [];
let pages = [];

// Get pages using getStaticProps
const { routes } = getPrerenderManifest();
Expand Down
Loading