scullyio / scully

The Static Site Generator for Angular apps
https://scully.io/
MIT License
2.55k stars 257 forks source link

Angular i18n support #318

Closed BlindDespair closed 4 years ago

BlindDespair commented 4 years ago

🧩 Feature request

Description

Angular 9 allows building the app like ng build --localize to enable localization. In previous versions of anular a separate build for locale was needed but output was similar. Angular 9 allows to specify locales for i18n in angular.json in a following way:

"i18n": {
    "sourceLocale": { "code": "ru", "baseHref": "/" },
    "locales": {
      "uk": "",
      "en": "" // leaving these empty because I am not going to use local translation files, but will rely on a headless cms instead
    }
},

Building angular with --localize then results in following dist structure: ./dist/{project-name}/{locale-id}. Scully is not possible to configure for this case at the moment. Scully checks only ./dist/{project-name} by default. Besides that scully does not seem to have a mechanism to tell it about baseHref that project has from a config file. Here is example of the dist structure: image

Describe the solution you'd like

There a couple of options we could have here:

  1. E.g. we could have a posibility to tell scully in the config file that it's an i18n dist it will work with (or it could possibly figure it out on its own). Then scully output would have to replicate the dist structure, e.g. if I specify outDir: './dist/static' it would do the following: ./dist/static/{locale-id}. Scully should be able to respect baseHref in this case (we can provide it in config file or maybe scully can read it from angular.json), but there can be other use cases when users might manually set base href in index.html.
  2. Possibly CLI arguments and config file have to be brought to consistency, meaning that options available in config file should be available in CLI and vice versa. That would allow us using different CLI command to prerender per each locale.

Describe alternatives you've considered

Currently this behaviour is possible to achieve in combination of using `distFolder` and `outDir` settings in `scully.config.js` and `--baseFilter` option in CLI command (although naming of this argument is not clear what its intension is, maybe it is just a happy coincidence that it works). Here is an example config: ``` exports.config = { projectRoot: './', outDir: './dist/static/en', distFolder: './dist/frontend/en', routes: {}, puppeteerLaunchOptions: { args: ['--no-sandbox', '--disable-setuid-sandbox'], }, }; ``` which if ran with `npm run scully -- --bf /en` could give a desired result, but I believe this can get really messy when projects will have more locales (in my case I have 3). It is also possible to further improve the situation using `process.env`: ``` exports.config = { projectRoot: './', outDir: `./dist/static/${process.env.LOCALE}`, distFolder: `./dist/frontend/${process.env.LOCALE}`, routes: {}, puppeteerLaunchOptions: { args: ['--no-sandbox', '--disable-setuid-sandbox'], }, }; ``` which can then be executed like: `export LOCALE=en && npm run scully --bf /$LOCALE` This is then cleaner and does not require a config file per locale, but does require running scully once per locale. Delivery Goal - 07/16/2020
BlindDespair commented 4 years ago

Actually using --baseFilter CLI argument is not working. Scully is then unable to find any routes. So in the end my alternative solution is not working unfortunately. That makes sense because --baseFilter is supposed to be a route, it's not same as baseHref. But problem is that there doesn't seem to be an option to tell scully about baseHref currently.

SanderElias commented 4 years ago

@BlindDespair Thanks for the issue. I will look into this. Can you explain why you need a different base-ref for every language? That part is still unclear to me. I do have a couple of ideas on how to "fix" his issue.

BlindDespair commented 4 years ago

@SanderElias Different base href is needed because the directories are generated per locale, which means you have a different angular application per locale. There are only 2 ways to tell webserver which angular app to use for which locale:

  1. Using subdomain (then baseHref can be same)
  2. Using subdirectory (then have to specify baseHref so that angular will still work correctly with the same paths, meaning that when you have your css in the same folder it will be loaded from /en/path and not /path)

I am going to stick with subdirectories for this, thus need baseHref.

ftanrisevdi commented 4 years ago

Hi @SanderElias, I also really need this support. Is there any progress? At least passing arguments to scully.config.js could be very useful. Thank you in advance

dyingangel666 commented 4 years ago

@BlindDespair have the same build with i18n... and configured all similar to your stuff. The problem now is that when the angular project was built with --localize flag, the baseHref's are automatically set to the respective language, but in this case scully can't parse the pages correctly in my case which results, that the <app-root> element in the generated index.html files from scully has no childs. Do you have the same issues, or is this working in your project?

@SanderElias The different baseHref is automatically set by angular, when the --localize flag is set. For example, you have two languages configured in your i18n config part in the angular.json (de + en), then angular builds two separate projects into subfolders dist/project/de + dist/project/en and automatically sets the baseHref to /de/ and /en/ for the respective language projects

spock123 commented 4 years ago

Also stuck on this, too bad I really wanted to try to statically render our site.. does anyone know of a workaround for this? Even having a separate scully config for each locale (with an outdir specification) would be good enough

dyingangel666 commented 4 years ago

Also stuck on this, too bad I really wanted to try to statically render our site.. does anyone know of a workaround for this? Even having a separate scully config for each locale (with an outdir specification) would be good enough

Yeah but there's another issue with parameterized scully config and the localized sites are not correctly parsed. Also trying to workaround it the last days...

BlindDespair commented 4 years ago

@dyingangel666 I don't have a working solution yet because there is no chance to tell scully what baseHref to use. I am still waiting for this too. We need multilanguage support on our site, but it's not high prio.

dyingangel666 commented 4 years ago

@dyingangel666 I don't have a working solution yet because there is no chance to tell scully what baseHref to use. I am still waiting for this too. We need multilanguage support on our site, but it's not high prio.

Yeah same here....But for me it's very important because it's a marketplace site. Trying another thing tomorrow to workaround the issue... Maybe I have luck 😅 the scully build works correctly when the baseHref is set to "/" but then some other stuff can't be referenced. My idea is to set the baseHref and then use a post script to change it back to the correct one like "/de/" after it's successfully built

dyingangel666 commented 4 years ago

So found a workaround that is working. Not the best solution, but until there's a fix from scully side, it's the best option for now:

scully.config

require('./postrender-plugins/i18nBaseHrefFix');

exports.config = {
  projectRoot: "./src",
  defaultPostRenderers: [
    'i18nBaseHrefFix'
  ],
  projectName: "marketplace-frontend",
  outDir: `./dist/static/${process.env.LOCALE}`,
  distFolder: `./dist/marketplace-frontend/${process.env.LOCALE}`,
  routes: {
    '/products/:productId': {
      type: 'json',
      productId: {
        url: 'http://localhost:1668/assets/products.json',
        property: 'id'
      }
    }
  },
  reloadPort: 4200,
  puppeteerLaunchOptions: {
    args: ['--no-sandbox', '--disable-setuid-sandbox']
  }
};

i18nBaseHrefFix.js

const {registerPlugin} = require('@scullyio/scully');

const i18nBaseHrefFixPlugin = async (html) =>
{
  const regex = new RegExp(`(<base href=")(\\/)(">)`, 'gmi');
  html = html.replace(regex, `$1/${process.env.LOCALE}/$3`);

  return Promise.resolve(html);
};

registerPlugin('render', 'i18nBaseHrefFix', i18nBaseHrefFixPlugin);
module.exports.i18nBaseHrefFixPlugin = i18nBaseHrefFixPlugin;

angular.json (i18n config)

"i18n": {
        "sourceLocale": {
          "code": "de",
          "baseHref": "/"
        },
        "locales": {
          "en": {
            "translation": "src/locales/messages.en.xlf",
            "baseHref": "/"
          }
        }
      }

It's important to set the baseHref for each language to "/" because then scully parses all pages correctly and the index.html files have all the static content. The postrenderer script then sets the baseHref correctly back to the respective language, so that all references are still working. The scully config is parametrized (thx for @BlindDespair for this idea) ;-)

Cheers

SanderElias commented 4 years ago

I have released a base-href-rewrite plugin,(#610) that should take care of most of the concerns here. (instructions)

aaronfrost commented 4 years ago

@SanderElias let's have some more conversation with the people on this issue. Let's see if the baseHrefRewrite functionality gets them what they need. If so, let's close this. If not, let's finish building what they need for i18n.

aaronfrost commented 4 years ago

@BlindDespair can you comment here on your latest progress or lack thereof? We want to fix this, but need you to give feedback here. We think we may be done. But we also think there may be a few more things you need from us. We need to understand this.

BlindDespair commented 4 years ago

@aaronfrost I can try out the baseHref plugin some time during the weekend to see if it's enough for me. If it works, I think I will be happy with it. I think prerendering for each language separately actually gives an advantage because you may not always need to regenerate all of them and run jobs for each language in parallel even on different runners. I will write as soon as I check it.

BlindDespair commented 4 years ago

I tried it out, but so far I haven't managed to get it to work. When I use the base href plugin the pages just don't get prerendered. I did the following:

  1. Built angular using ng build --localize --stats-json -c staging (generates angular with all locals, e.g. ./dist/frontend/uk)
  2. Updated my scully config to use the baseHref plugin with defaultPostRenderers: [baseHrefRewrite] (also added this line setPluginConfig(baseHrefRewrite, { href: process.env.BASE_HREF });)
  3. Added distFolder: `./dist/frontend/${process.env.LOCALE}`, to get the desired language's angular build
  4. Triggered prerendering with SCULLY_ENV=staging LOCALE=uk npx scully --scanRoutes

The prerendering is running, but appears to be very slow. After the generation is over index.html filles contain no prerendered DOM. Just the basic index.html of angular. And because of that data.json wasn't generated. Thus when serving scully build it's not working at all. I tried running with --showBrowser and saw that the URL used didn't icnlude baseHref and the generation didn't even succeed. If you can somehow help me solving it, would be great.

Some more notes: I believe it's possible to make a working i18n using what's available in scully assuming it works, but I do see certain inconviniences.

  1. We need to either create multiple config files or use environment variables utilizing something like dotenv. Information about what baseHref is needed and what is the dist subfolder is already there in angular.json. Ideally scully could read it. I would need at least 2 dotenv variables: LOCALE for dist subpath and passing to plugins and BASE_HREF for passing it to baseHref rewrite plugin because it might not always be the same. See my angular.json i18n:
    "i18n": {
      "sourceLocale": { "code": "ru", "baseHref": "/" }, // BASE_HREF = '/', LOCALE = 'ru'
      "locales": {
          "uk": "", // BASE_HREF = 'uk', LOCALE = 'uk'
          "en": "" // // BASE_HREF = 'en', LOCALE = 'en'
      }
    },
  2. Scully generates the static content into dist/static. This is not too bad, but it requires an extra step on the CI to move the files, because I think the desired structure would be something like dist/static/:locale, because that's how in the last job, I'll just copy over the whole static folder to my server.
  3. I realized that there may be a need to pass locale parameters to a plugin... Let's say a slug for URL is different per language for better SEO. I have something like this:
    '/menu/:categorySlug': {
      type: 'json',
      categorySlug: {
        url: `${process.env.API_BASE_URL}menu/categories?lang=${[process.env.LOCALE}`,
        property: 'urlSegment',
      },
    },

    Since I am using dotenv here, currently it is not a problem, however, if scully was using angular.json and I wouldn't need dotenv there how would I be able to get locale?

To sum up: Best would be if scully had a simple config to handle i18n stuff in just a couple of steps without having scully user doing multiple manual steps, however, use cases like in note 3 would need to be considered. The most desired CLI imo would look something like npx scully --locale uk (everything else is already in angular.json) if no locale is specified, then the default should be used (like here: "sourceLocale": { "code": "ru", "baseHref": "/" },).

@SanderElias what are your thoughts?

SanderElias commented 4 years ago

@BlindDespair The plugin alters the base-href after rendering. For the before part, that should have been taken care of by the CLI. I saw you are setting the 'dist' folder to the language folders. This seems Ok to me, but did angular set an 'base-href' in there? It should be / in that case. If the base/href in those folders is for example /uk/, there might be an issue.

dyingangel666 commented 4 years ago

@SanderElias If you're building your angular project with the --localize flag, angular automatically builds a separate project for each language which is defined in your angular.json file. Angular builds the project for each language into a subfolder with a two letter locale string (e.g en, de, it, pl) and also sets the baseHref for each project to this two letter locale. Also tried out your plugin but actually it doesn't work as expected for us.

I think this should be implemented directly in the scully core and not as a plugin. During the scully build, scully should detect if a localize settings exists in the angular.json file, and then also recognize that the dist folder have multiple subfolders for each language and then scully should automatically build the static sites for each languages.

Your baseHref plugin basically do the same as my own... The problem also is, when the baseHrefs are en, de or whatever (which is set automatically by angular cli), then scully cannot parse the content of the pages and the prerendered static pages are empty within the <app-root>. I could fix this when i set the baseHref for each language in the angular.json file to "/" and then my/your basehref plugin sets the correct baseHref again for each page. But then we come to the next issue, all href links in the prerendered static pages not starting with /de/whatever/ or /en/whatever/, and are just /whatever/ and that's wrong. I now end up with another plugin which fixes all links in tags, and all looks fine for me.

const {registerPlugin} = require('@scullyio/scully');

const fixStaticLinksPlugin = async (html) => {
  const regex = new RegExp('(<a[^>]* href="\/)([^"]*)"', 'gmi');
  html = html.replace(regex, `$1${process.env.LOCALE}/$2"`);

  return Promise.resolve(html);
};

registerPlugin('render', 'fixStaticLinks', fixStaticLinksPlugin);
module.exports.fixStaticLinksPlugin = fixStaticLinksPlugin;
amycdurbin commented 4 years ago

Goal Deliverable: 2020-07-16

tpiros commented 3 years ago

I'm sorry for responding to an already closed issue. I have read through this thread but I'm not 100% sure if I see the solution. Is it the combination of using the the base-href-rewrite plugin and the RegEx by @dyingangel666?

I'm having the same problem where I'm using Angular's i18n, get two folders when doing a build and running Scully doesn't exactly give me the right result. I have tried the above solutions but I end up getting one single static folder with the last language in my list to be the one that get statically generated. I'd love to have a walkthrough of what are the right steps to fix this particular issue. (In particular, how the angular.json file is setup, how the baseHref is setup [if it's setup via APP_BASE_HREF], what the Scully config looks like and what is the command that needs to be executed. I think I'm close to getting this to work, I just need one last thing :)

dyingangel666 commented 3 years ago

@tpiros Ok let's sumarize all things from my working project:

The package.json with some build stuff:

{
  "name": "marketplace-frontend",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "npm run getproducts; ng serve --host 0.0.0.0",
    "start:noupdate": "ng serve --host 0.0.0.0 --port 4201",
    "build": "npm run clear:dist; npm run getproducts; ng build --prod --localize --stats-json; env SET_PUPPETEER_EXECUTABLE_PATH=true npm run scully:de; npm run scully:en; gzipper compress --brotli --include js,css,html --level 6 ./dist/static;",
    "buildLocal": "npm run clear:dist; npm run getproducts; ng build --prod --localize --stats-json; env SET_PUPPETEER_EXECUTABLE_PATH=false npm run scully:de; npm run scully:en; gzipper compress --brotli --include js,css,html --level 6 ./dist/static;",
    "buildLocal:noupdate": "ng build --prod --localize --stats-json; env SET_PUPPETEER_EXECUTABLE_PATH=false npm run scully:de; npm run scully:en",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "clear:dist": "npx rimraf ./dist",
    "copyfiles": "copy-files-from-to --when-file-exists overwrite",
    "prepareproducts": "node product-data-preprocessor.js",
    "getproducts:de": "npx mkdirp ./dist/marketplace-frontend/de/assets; curl -m 900 -o ./dist/marketplace-frontend/de/assets/products.json https://app.softgarden.io/api/rest/backend/v3/marketplace/de",
    "getproducts:en": "npx mkdirp ./dist/marketplace-frontend/en/assets; curl -m 900 -o ./dist/marketplace-frontend/en/assets/products.json https://app.softgarden.io/api/rest/backend/v3/marketplace/en",
    "getproducts": "npm run getproducts:de; npm run getproducts:en; npm run prepareproducts; npm run copyfiles",
    "scully:de": "env URLPREFIX=https://marketplace.softgarden.io env LOCALE=de scully --scanRoutes",
    "scully:en": "env URLPREFIX=https://marketplace.softgarden.io env LOCALE=en scully --scanRoutes",
    "scully:serve": "scully serve",
    "i18n": "ng xi18n --format=xlf --output-path=src/locales --out-file=messages.xlf",
    "xliffmerge": "ng run marketplace-frontend:xliffmerge",
    "translate": "npm run i18n; npm run xliffmerge",
    "local": "docker-compose down; docker-compose pull; docker-compose up --build"
  },
  "copyFiles": [
    {
      "from": "dist/marketplace-frontend/de/assets/products.json",
      "to": "src/assets/products.json"
    },
    {
      "from": "./src/root/*",
      "to": "./dist/static/"
    }
  ],
  "private": true,
  "dependencies": {
    "@angular/animations": "10.2.1",
    "@angular/cdk": "10.2.7",
    "@angular/common": "10.2.1",
    "@angular/compiler": "10.2.1",
    "@angular/core": "10.2.1",
    "@angular/forms": "10.2.1",
    "@angular/localize": "10.2.1",
    "@angular/platform-browser": "10.2.1",
    "@angular/platform-browser-dynamic": "10.2.1",
    "@angular/router": "10.2.1",
    "@scullyio/init": "1.0.1",
    "@scullyio/ng-lib": "1.0.0",
    "@scullyio/scully": "1.0.4",
    "lazysizes": "5.2.1",
    "new-event-polyfill": "1.0.1",
    "normalize.css": "8.0.1",
    "rxjs": "6.5.4",
    "tslib": "1.10.0",
    "zone.js": "0.10.2"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "0.1002.0",
    "@angular/cli": "10.2.0",
    "@angular/compiler-cli": "10.2.1",
    "@angular/language-service": "10.2.1",
    "@ngx-i18nsupport/ngx-i18nsupport": "1.1.6",
    "@ngx-i18nsupport/tooling": "8.0.3",
    "@scullyio/scully-plugin-base-href-rewrite": "0.0.2",
    "@types/jasmine": "~3.5.0",
    "@types/jasminewd2": "~2.0.3",
    "@types/node": "^12.11.1",
    "codelyzer": "5.2.1",
    "copy-files-from-to": "3.2.0",
    "gzipper": "^4.3.0",
    "jasmine-core": "~3.6.0",
    "jasmine-spec-reporter": "~5.0.0",
    "karma": "~5.0.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage-istanbul-reporter": "~3.0.2",
    "karma-jasmine": "~4.0.0",
    "karma-jasmine-html-reporter": "^1.5.0",
    "image-downloader": "4.0.1",
    "imagemin": "7.0.1",
    "imagemin-mozjpeg": "9.0.0",
    "imagemin-pngquant": "9.0.1",
    "protractor": "5.4.3",
    "resize-img": "2.0.0",
    "rimraf": "3.0.2",
    "scully-plugin-minify-html": "3.1.2",
    "ts-node": "~8.3.0",
    "tslint": "~6.1.0",
    "typescript": "~4.0.2",
    "update-json-file": "1.1.1"
  }
}

Then the scully config (ts version):

import {ScullyConfig, setPluginConfig} from '@scullyio/scully';
import {MinifyHtml} from 'scully-plugin-minify-html';
import {baseHrefRewrite} from '@scullyio/scully-plugin-base-href-rewrite';

require('./postrender-plugins/fixStaticLinks');
require('./postrender-plugins/sitemapGenerator');

// @ts-ignore
setPluginConfig(baseHrefRewrite, {href: `/${process.env.LOCALE}/`});

export const config: ScullyConfig = {
  projectRoot: './src',
  defaultPostRenderers: [
    'seoHrefOptimise',
    'sitemapGenerator',
    'fixStaticLinks',
    // @ts-ignore
    baseHrefRewrite,
    MinifyHtml,
  ],
  inlineStateOnly: true,
  projectName: 'marketplace-frontend',
  outDir: `./dist/static/${process.env.LOCALE}`,
  distFolder: `./dist/marketplace-frontend/${process.env.LOCALE}`,
  reloadPort: 4200,
  staticPort: 1668,
  routes: {
    '/:productId': {
      type: 'json',
      productId: {
        url: 'http://localhost:1668/assets/products.json',
        property: 'id'
      }
    }
  },
  puppeteerLaunchOptions: {
    executablePath: process.env.PUPPETEER_EXECUTABLE_PATH ? 'google-chrome-stable' : '',
    args: ['--no-sandbox', '--disable-setuid-sandbox']
  }
};

And finally the angular.json:

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "marketplace-frontend": {
      "projectType": "application",
      "schematics": {
        "@schematics/angular:component": {
          "style": "scss"
        }
      },
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "i18n": {
        "sourceLocale": {
          "code": "de",
          "baseHref": "/"
        },
        "locales": {
          "en": {
            "translation": "src/locales/messages.en.xlf",
            "baseHref": "/"
          }
        }
      },
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/marketplace-frontend",
            "resourcesOutputPath": "assets/fonts",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.app.json",
            "aot": true,
            "assets": [
              "src/assets"
            ],
            "styles": [
              "src/styles.scss"
            ],
            "scripts": [],
            "stylePreprocessorOptions": {
              "includePaths": [
                "src/app"
              ]
            }
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "assets": [
                { "glob": "**/*", "input": "src/assets/", "ignore": ["products.json"], "output": "/assets/" }
              ],
              "deleteOutputPath": false,
              "optimization": true,
              "outputHashing": "bundles",
              "sourceMap": false,
              "extractCss": false,
              "namedChunks": false,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "2mb",
                  "maximumError": "5mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "6kb",
                  "maximumError": "10kb"
                }
              ]
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "marketplace-frontend:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "marketplace-frontend:build:production"
            }
          }
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "browserTarget": "marketplace-frontend:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "src/test.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.spec.json",
            "karmaConfig": "karma.conf.js",
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.scss"
            ],
            "scripts": []
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": [
              "tsconfig.app.json",
              "tsconfig.spec.json"
            ],
            "exclude": [
              "**/node_modules/**"
            ]
          }
        },
        "xliffmerge": {
          "builder": "@ngx-i18nsupport/tooling:xliffmerge",
          "options": {
            "xliffmergeOptions": {
              "srcDir": "src/locales",
              "genDir": "src/locales",
              "i18nFile": "messages.xlf",
              "i18nBaseFile": "messages",
              "i18nFormat": "xlf",
              "encoding": "UTF-8",
              "defaultLanguage": "de",
              "languages": [
                "en"
              ],
              "removeUnusedIds": true,
              "supportNgxTranslate": false,
              "ngxTranslateExtractionPattern": "@@|ngx-translate",
              "useSourceAsTarget": true,
              "targetPraefix": "",
              "targetSuffix": "",
              "beautifyOutput": true,
              "allowIdChange": false,
              "autotranslate": false,
              "apikey": "",
              "apikeyfile": "",
              "verbose": false,
              "quiet": false
            }
          }
        }
      }
    }
  },
  "defaultProject": "marketplace-frontend"
}

And here the code for my fixStaticLinks.js file:

const {registerPlugin} = require('@scullyio/scully');

const fixStaticLinksPlugin = async (html) => {
  const regex = new RegExp('(<a[^>]* href="\/)([^"]*)"', 'gmi');
  html = html.replace(regex, `$1${process.env.LOCALE}/$2"`);

  return Promise.resolve(html);
};

registerPlugin('render', 'fixStaticLinks', fixStaticLinksPlugin);
module.exports.fixStaticLinksPlugin = fixStaticLinksPlugin;
tpiros commented 3 years ago

Thank you @dyingangel666! I found what was missing at my end ... it was the pesky seoHrefOptimise renderer. I didn't notice that I was missing that up until now ... 🙄 thank you again!

tpiros commented 3 years ago

Just curious @dyingangel666 (and if you're on some Slack or Discord channel where we can converse, let me know) but, does your Scully command "hang"? I get the routes generated and everything seem to be working fine except in scully.log I get Received Kill command and the process hangs for about 1-2 minutes and after it just quits. Not sure why this is but I'm not getting the same behaviour with non i18n projects.

dyingangel666 commented 3 years ago

@tpiros Mh nope in my project everything is working and running successfully... Can you post the complete error log?

tpiros commented 3 years ago

@dyingangel666 oddly enough, it turned out to be a process that got "stuck". For reasons unknown, the ScullyServer process didn't shut down and was running and while I started it again, it would just keep on crashing. Good 'ol pkill -9 ScullyServer did the job ...

tpiros commented 3 years ago

hi @dyingangel666 - one question. When you deployed your project, did you ever run into an issue where only one language would get pre-rendered and the other wouldn't? I'm having some funky issues when deploying to Amplify - one language doesn't pre-render the other does. Tt's almost like whatever I put in as the second export LANG=xx && npm run scully bit wins.

dyingangel666 commented 3 years ago

@tpiros Mhhh nope no issues like that in my project. All languages successfully built in their respective subfolders... Was the default angular project built successfully with all languages and just within the scully build something goes wrong?

tpiros commented 3 years ago

I'm trying to debug that. When deploying to Amplify, I need to run npm run scully once with one language in order to get pre-rendering. so the build script looks like this now:

"scully:en": "export LOCALE=en && npm run scully",
"scully:es": "export LOCALE=es && npm run scully",
"static": "ng build --prod --localize; npm run scully:en; npm run scully:en; npm run scully:es"

I'm running some tests - not sure what has gone wrong ... :(

dyingangel666 commented 3 years ago

Mhhh did you also tried to run the commands sequentially with &&:

ng build --prod --localize && npm run scully:en && npm run scully:en && npm run scully:es

Not sure if it makes a difference... but it's worth a try. Can you show me all npm scripts?

tpiros commented 3 years ago

yes, that was the original, didn't make a difference. (theoretically ; runs the script regardless of the success of the previous one, whereas && only runs if the previous is successful)

This is the version that doesn't work. The EN site is empty the articles only load when JS is enabled, the ES site works both ways since the content is pre-rendered.

I have a feeling it has to do with my implementation of the service that gets this data, because I've done a test with a simpler API and that seems to be working just fine. FYI I'm using Storyblok as my CMS where I'm fetching the data from.

"scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "scully": "scully",
    "scully:serve": "scully serve",
    "scully:en": "export LOCALE=en && npm run scully",
    "scully:es": "export LOCALE=es && npm run scully",
    "static": "ng build --prod --localize; npm run scully:en; npm run scully:es"
  },
tpiros commented 3 years ago

Found the culprit! It's the Storyblok JS client ... not sure why it works if I run scully 3x and why it doesn't work if I run it just twice...I'll talk to their devs and try to figure it out. What bugs me is that I don't see a difference between my local environment and Amplify.