Static Site Generation (SSG) in Angular with Angular Internationalization (i18n) and Prerendering
Sehban Alam
Software Engineer (Creating Scalable, Secure, Cloud-Powered Applications that Redefine B2B/B2C User Experiences) | Angular | Firebase | Cloudflare | Material Design | Serverless | MySQL | GCP | AWS
With the increasing demand for fast, SEO-friendly web applications, Static Site Generation (SSG) has become an essential technique in modern web development. Angular, a powerful framework for building dynamic applications, now supports SSG through Angular Universal. When combined with Angular’s internationalization (i18n) capabilities and the ability to define prerendering paths and hydration, you can create a highly optimized, multi-lingual static site.
This blog will guide you step-by-step on how to implement SSG in an Angular project with i18n, define prerendering paths, enable hydration, and deploy it on Cloudflare Pages.
Table of Contents
What is SSG?
Static Site Generation (SSG) is a technique where web pages are generated at build time. Instead of serving HTML dynamically for each request, pre-rendered static HTML files are served, making your website faster and more scalable.
When combined with i18n, SSG can generate static versions of your site in multiple languages. Adding prerendering paths ensures that all routes in your application are generated as static files, while hydration improves the user experience by making the static content interactive after it has been rendered.
Prerequisites
Step 1: Setting Up an Angular Project
First, we need to create a new Angular project. If you already have an existing project, you can skip this step.
ng new angular-ssg-i18n
cd angular-ssg-i18n
During project setup, Angular will ask if you want to enable Angular routing. Select “Yes” and choose CSS as your preferred style format.
Step 2: Adding Angular Universal for SSG
Angular Universal allows you to render Angular applications on the server. It is a crucial part of setting up SSG.
Add Angular Universal to your project:
ng add @nguniversal/express-engine
This command configures Angular Universal and sets up an Express server for serving your application.
Step 3: Configuring Angular i18n
Angular’s built-in i18n module helps you support multiple languages within your app. Let’s configure it:
Add translations files:
Create translation files for the languages you want to support. For example:
You can extract the default translations using:
ng extract-i18n --output-path src/locale
This will generate the default messages.xlf file in the specified path.
Configure i18n in angular.json:
Add new configurations for each language in the build section of angular.json:
"build": {
"configurations": {
"en": {
"aot": true,
"outputPath": "dist/angular-ssg-i18n/en",
"i18nFile": "src/locale/messages.en.xlf",
"i18nLocale": "en",
"i18nMissingTranslation": "warning"
},
"fr": {
"aot": true,
"outputPath": "dist/angular-ssg-i18n/fr",
"i18nFile": "src/locale/messages.fr.xlf",
"i18nLocale": "fr",
"i18nMissingTranslation": "warning"
}
}
}
Update the App Module:
In your app.module.ts, import LOCALE_ID and provide it dynamically:
import { LOCALE_ID } from '@angular/core';
@NgModule({
providers: [
{ provide: LOCALE_ID, useValue: 'en' }
]
})
export class AppModule { }
You will change this value dynamically later for different languages.
Step 4: Adding Prerendering Paths and Hydration
To enable Angular’s prerendering capabilities, you need to define which routes to prerender and ensure your application hydrates the static content into a dynamic one.
1. Defining Prerendering Routes:
Update angular.json to specify which routes should be prerendered. In the "prerender" configuration, you can define static routes for each language. Here's an example of how to specify multiple routes:
"architect": {
"prerender": {
"options": {
"routes": [
"/",
"/about",
"/contact"
]
}
}
}
You can dynamically specify routes based on the languages and pages you want to prerender.
2. Hydrating the Content:
Angular Universal already handles hydration, where the pre-rendered HTML is enhanced by Angular once JavaScript is fully loaded. However, you need to ensure that lazy-loaded modules are properly handled. Make sure your AppModule and AppComponent are properly set up for hydration.
To improve hydration for dynamic content like forms or dynamic UI components, you can manually trigger rehydration using Angular services after static content has loaded.
For example, if you have a dynamic form or UI component, you can handle it this way:
export class DynamicFormComponent implements OnInit {
isBrowser: boolean;
constructor(@Inject(PLATFORM_ID) private platformId: Object) {
this.isBrowser = isPlatformBrowser(this.platformId);
}
ngOnInit(): void {
if (this.isBrowser) {
// Dynamic content goes here after hydration
}
}
}
Step 5: Building SSG with i18n
Now that i18n, prerendering paths, and hydration are set up, it’s time to build and generate the static pages.
领英推荐
Create production builds for each locale:
For each language, run the following command:
ng build --configuration=en
ng build --configuration=fr
Generate server-side rendering builds:
You also need to build the server part of your application:
ng run angular-ssg-i18n:server:production
Prerender the static content:
Angular provides a command to prerender the application:
npm run prerender
The prerendering process will generate static HTML files for each route in your application and for each language.
Step 6: Deploying the SSG Build on Cloudflare Pages
Cloudflare Pages is an ideal platform for deploying static sites. Here’s how to deploy your multi-lingual static site generated by Angular with SSG.
Create a new Git repository: First, initialize a Git repository for your Angular project (if not already done):
git init
git add .
git commit -m "Initial commit"
Push the project to GitHub: Create a repository on GitHub and push your project:
git remote add origin https://github.com/your-username/angular-ssg-i18n.git
git push -u origin master
Set up Cloudflare Pages:
npm run prerender
dist/angular-ssg-i18n
Customize build settings for each language: If needed, you can create additional deployments for each language (e.g., dist/angular-ssg-i18n/en, dist/angular-ssg-i18n/fr) or configure routing rules in Cloudflare Pages to automatically serve the correct language based on the request’s locale.
Deploy the project: Once everything is set up, click Save and Deploy. Cloudflare will build and deploy your project automatically. Once completed, your static, multi-lingual Angular site will be live!
Complete Code Sections
Here are the final configurations for your key files:
In the angular.json, you should include multiple language configurations and define the prerendering options:
{
"projects": {
"angular-ssg-i18n": {
"architect": {
"build": {
"configurations": {
"en": {
"aot": true,
"outputPath": "dist/angular-ssg-i18n/en",
"i18nFile": "src/locale/messages.en.xlf",
"i18nLocale": "en",
"i18nMissingTranslation": "warning"
},
"fr": {
"aot": true,
"outputPath": "dist/angular-ssg-i18n/fr",
"i18nFile": "src/locale/messages.fr.xlf",
"i18nLocale": "fr",
"i18nMissingTranslation": "warning"
}
}
},
"prerender": {
"builder": "@nguniversal/builders:prerender",
"options": {
"routes": [
"/",
"/about",
"/contact"
]
}
}
}
}
}
}
This setup defines builds for both English and French and configures prerendering for the routes /, /about, and /contact.
app.module.ts
Here’s an updated app.module.ts that supports dynamic locale setting using LOCALE_ID:
import { NgModule, LOCALE_ID } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { registerLocaleData } from '@angular/common';
import localeEn from '@angular/common/locales/en';
import localeFr from '@angular/common/locales/fr';
registerLocaleData(localeEn);
registerLocaleData(localeFr);
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [
{
provide: LOCALE_ID,
useFactory: () => {
const defaultLocale = 'en';
return localStorage.getItem('locale') || defaultLocale;
}
}
],
bootstrap: [AppComponent]
})
export class AppModule {}
In this example, LOCALE_ID is dynamically determined using localStorage or defaults to 'en' if not set.
Dynamic Form Component for Hydration
import { Component, Inject, PLATFORM_ID, OnInit } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
@Component({
selector: 'app-dynamic-form',
templateUrl: './dynamic-form.component.html'
})
export class DynamicFormComponent implements OnInit {
isBrowser: boolean;
constructor(@Inject(PLATFORM_ID) private platformId: Object) {
this.isBrowser = isPlatformBrowser(this.platformId);
}
ngOnInit(): void {
if (this.isBrowser) {
// Hydrate the static content with dynamic interactions here
console.log('Hydration complete for dynamic form');
}
}
}
Step 7: Deploying the SSG Build on Cloudflare Pages (continued)
After configuring your prerendered routes, i18n settings, and dynamic content hydration, you are ready to deploy the application to Cloudflare Pages.
Set Build Commands in Cloudflare Pages
When setting up Cloudflare Pages, ensure you use the right build commands for both the server-side rendering and static site generation.
Build Command:
npm run prerender
Output Directory:
For example, if you’re generating output for English:
dist/angular-ssg-i18n/en
If deploying multiple locales, create separate deployments or manage them through routing rules on Cloudflare Pages, depending on your deployment strategy.
Conclusion
By following the steps in this article, you can implement Static Site Generation (SSG) in an Angular application with Angular Universal, support multiple languages with Angular’s i18n, and optimize for performance and SEO through prerendering and hydration. Additionally, deploying the final build to Cloudflare Pages allows you to serve a blazing-fast, SEO-friendly website to a global audience.
?? Sviluppatore web | Programmatore web | Freelance ? Front-end: Angular/TypeScript ? Back-end: ASP.NET MVC CORE C# ? Fullstack Software Engineer
6 个月Thanks for sharing. ??