@@ -725,6 +725,8 @@ export default async function build(
725725 // n.b. we cannot handle this above in combinedPages because the dynamic
726726 // page must be in the `pages` array, but not in the mapping.
727727 exportPathMap : ( defaultMap : any ) => {
728+ const { i18n } = config . experimental
729+
728730 // Dynamically routed pages should be prerendered to be used as
729731 // a client-side skeleton (fallback) while data is being fetched.
730732 // This ensures the end-user never sees a 500 or slow response from the
@@ -738,7 +740,14 @@ export default async function build(
738740 if ( ssgStaticFallbackPages . has ( page ) ) {
739741 // Override the rendering for the dynamic page to be treated as a
740742 // fallback render.
741- defaultMap [ page ] = { page, query : { __nextFallback : true } }
743+ if ( i18n ) {
744+ defaultMap [ `/${ i18n . defaultLocale } ${ page } ` ] = {
745+ page,
746+ query : { __nextFallback : true } ,
747+ }
748+ } else {
749+ defaultMap [ page ] = { page, query : { __nextFallback : true } }
750+ }
742751 } else {
743752 // Remove dynamically routed pages from the default path map when
744753 // fallback behavior is disabled.
@@ -760,6 +769,39 @@ export default async function build(
760769 }
761770 }
762771
772+ if ( i18n ) {
773+ for ( const page of [
774+ ...staticPages ,
775+ ...ssgPages ,
776+ ...( useStatic404 ? [ '/404' ] : [ ] ) ,
777+ ] ) {
778+ const isSsg = ssgPages . has ( page )
779+ const isDynamic = isDynamicRoute ( page )
780+ const isFallback = isSsg && ssgStaticFallbackPages . has ( page )
781+
782+ for ( const locale of i18n . locales ) {
783+ if ( ! isSsg && locale === i18n . defaultLocale ) continue
784+ // skip fallback generation for SSG pages without fallback mode
785+ if ( isSsg && isDynamic && ! isFallback ) continue
786+ const outputPath = `/${ locale } ${ page === '/' ? '' : page } `
787+
788+ defaultMap [ outputPath ] = {
789+ page : defaultMap [ page ] . page ,
790+ query : { __nextLocale : locale } ,
791+ }
792+
793+ if ( isFallback ) {
794+ defaultMap [ outputPath ] . query . __nextFallback = true
795+ }
796+ }
797+
798+ if ( isSsg && ! isFallback ) {
799+ // remove non-locale prefixed variant from defaultMap
800+ delete defaultMap [ page ]
801+ }
802+ }
803+ }
804+
763805 return defaultMap
764806 } ,
765807 trailingSlash : false ,
@@ -786,7 +828,8 @@ export default async function build(
786828 page : string ,
787829 file : string ,
788830 isSsg : boolean ,
789- ext : 'html' | 'json'
831+ ext : 'html' | 'json' ,
832+ additionalSsgFile = false
790833 ) => {
791834 file = `${ file } .${ ext } `
792835 const orig = path . join ( exportOptions . outdir , file )
@@ -820,8 +863,58 @@ export default async function build(
820863 if ( ! isSsg ) {
821864 pagesManifest [ page ] = relativeDest
822865 }
823- await promises . mkdir ( path . dirname ( dest ) , { recursive : true } )
824- await promises . rename ( orig , dest )
866+
867+ const { i18n } = config . experimental
868+
869+ // for SSG files with i18n the non-prerendered variants are
870+ // output with the locale prefixed so don't attempt moving
871+ // without the prefix
872+ if ( ! i18n || ! isSsg || additionalSsgFile ) {
873+ await promises . mkdir ( path . dirname ( dest ) , { recursive : true } )
874+ await promises . rename ( orig , dest )
875+ }
876+
877+ if ( i18n ) {
878+ if ( additionalSsgFile ) return
879+
880+ for ( const locale of i18n . locales ) {
881+ // auto-export default locale files exist at root
882+ // TODO: should these always be prefixed with locale
883+ // similar to SSG prerender/fallback files?
884+ if ( ! isSsg && locale === i18n . defaultLocale ) {
885+ continue
886+ }
887+
888+ const localeExt = page === '/' ? path . extname ( file ) : ''
889+ const relativeDestNoPages = relativeDest . substr ( 'pages/' . length )
890+
891+ const updatedRelativeDest = path . join (
892+ 'pages' ,
893+ locale + localeExt ,
894+ // if it's the top-most index page we want it to be locale.EXT
895+ // instead of locale/index.html
896+ page === '/' ? '' : relativeDestNoPages
897+ )
898+ const updatedOrig = path . join (
899+ exportOptions . outdir ,
900+ locale + localeExt ,
901+ page === '/' ? '' : file
902+ )
903+ const updatedDest = path . join (
904+ distDir ,
905+ isLikeServerless ? SERVERLESS_DIRECTORY : SERVER_DIRECTORY ,
906+ updatedRelativeDest
907+ )
908+
909+ if ( ! isSsg ) {
910+ pagesManifest [
911+ `/${ locale } ${ page === '/' ? '' : page } `
912+ ] = updatedRelativeDest
913+ }
914+ await promises . mkdir ( path . dirname ( updatedDest ) , { recursive : true } )
915+ await promises . rename ( updatedOrig , updatedDest )
916+ }
917+ }
825918 }
826919
827920 // Only move /404 to /404 when there is no custom 404 as in that case we don't know about the 404 page
@@ -877,13 +970,13 @@ export default async function build(
877970 const extraRoutes = additionalSsgPaths . get ( page ) || [ ]
878971 for ( const route of extraRoutes ) {
879972 const pageFile = normalizePagePath ( route )
880- await moveExportedPage ( page , route , pageFile , true , 'html' )
881- await moveExportedPage ( page , route , pageFile , true , 'json' )
973+ await moveExportedPage ( page , route , pageFile , true , 'html' , true )
974+ await moveExportedPage ( page , route , pageFile , true , 'json' , true )
882975
883976 if ( hasAmp ) {
884977 const ampPage = `${ pageFile } .amp`
885- await moveExportedPage ( page , ampPage , ampPage , true , 'html' )
886- await moveExportedPage ( page , ampPage , ampPage , true , 'json' )
978+ await moveExportedPage ( page , ampPage , ampPage , true , 'html' , true )
979+ await moveExportedPage ( page , ampPage , ampPage , true , 'json' , true )
887980 }
888981
889982 finalPrerenderRoutes [ route ] = {
0 commit comments