I have set up a project with angular 12 and use localization in it. I can successfully run english version of my site, but when I switch to other locales via button which changes url to be localhost/de server responds with many 404 for different files. When I switch back to en it works good.
Package.json
{
"name": "my-frontend",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve --configuration=development",
"build": "ng build --localize",
"transl": "ng extract-i18n --output-path locale --format=xlf2",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"lint": "ng lint",
"prepare": "husky install"
},
"private": true,
"dependencies": {
"@angular/animations": "~12.0.0",
"@angular/common": "~12.0.0",
"@angular/compiler": "~12.0.0",
"@angular/core": "~12.0.0",
"@angular/forms": "~12.0.0",
"@angular/platform-browser": "~12.0.0",
"@angular/platform-browser-dynamic": "~12.0.0",
"@angular/router": "~12.0.0",
"@angular/service-worker": "~12.0.0",
"@ngrx/effects": "^12.0.0",
"@ngrx/store": "^12.0.0",
"@ngrx/store-devtools": "^12.0.0",
"ramda": "^0.27.1",
"rxjs": "6.6.0",
"tslib": "^2.1.0",
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "~12.0.0",
"@angular-eslint/builder": "12.0.0",
"@angular-eslint/eslint-plugin": "12.0.0",
"@angular-eslint/eslint-plugin-template": "12.0.0",
"@angular-eslint/schematics": "12.0.0",
"@angular-eslint/template-parser": "12.0.0",
"@angular/cli": "~12.0.0",
"@angular/compiler-cli": "~12.0.0",
"@angular/localize": "12.0.0",
"@types/jasmine": "~3.6.0",
"@types/node": "^12.11.1",
"@types/ramda": "^0.27.40",
"@typescript-eslint/eslint-plugin": "4.23.0",
"@typescript-eslint/parser": "4.23.0",
"autoprefixer": "^10.2.5",
"eslint": "^7.26.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-ngrx": "^1.0.0",
"eslint-plugin-prettier": "^3.4.0",
"husky": "^6.0.0",
"jasmine-core": "~3.7.0",
"karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.0.3",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"postcss": "^8.2.15",
"postcss-loader": "^5.3.0",
"prettier": "^2.3.0",
"tailwindcss": "^2.1.2",
"typescript": "~4.2.3"
}
}
Dockerfile
# stage 1 for building the dist folder.
FROM node:14.17-alpine3.13 as my-front-dev
LABEL author="Victor Orlyk"
WORKDIR /app
COPY package.json package.json
COPY yarn.lock yarn.lock
RUN yarn install
#copy all code
COPY . .
RUN yarn run build
#Stage 2
FROM nginx:alpine
# cashing it is default anyway
VOLUME /var/cache/nginx
COPY --from=my-front-dev /app/dist/my-frontend/ /usr/share/nginx/my-frontend/
COPY ./config/nginx.conf /etc/nginx/conf.d/default.conf
# docker build -t nginx-angular -f dockerfile .
# docker run -p 8080:80 nginx-angular
Docker-compose.yml
# This can be used to run a development version of the Angular and Node containers
# See the readme.md for details on changes that are required in the Angular service
# Run docker-compose build
# Run docker-compose up
# Live long and prosper
version: '3.7'
services:
nginx:
container_name: nginx-angular
image: nginx-angular
build:
context: .
dockerfile: dockerfile
volumes:
- ./dist/my-frontend:/usr/share/nginx/html
ports:
- "80:80"
# depends_on:
# - node
networks:
- app-network
# node:
# container_name: angular-node-service
# image: angular-node-service
# build:
# context: ./server
# dockerfile: node.dockerfile
# environment:
# - NODE_ENV=development
# ports:
# - "3000:3000"
# networks:
# - app-network
# Disabled in case someone is running this on Windows (OK to uncomment if on Mac/Linux)
# cadvisor:
# container_name: cadvisor
# image: google/cadvisor
# volumes:
# - /:/rootfs:ro
# - /var/run:/var/run:rw
# - /sys:/sys:ro
# - /var/lib/docker/:/var/lib/docker:ro
# ports:
# - "8080:8080"
# networks:
# - app-network
networks:
app-network:
driver: bridge
config/nginx.conf
types {
module;
}
include /etc/nginx/mime.types;
# Expires map for caching resources
map $sent_http_content_type $expires {
default off;
text/html epoch;
text/css max;
application/javascript max;
~image/ max;
}
# Browser preferred language detection
map $http_accept_language $accept_language {
~*^en en;
~*^de de;
~*^ua ua;
}
server {
listen 80;
root /usr/share/nginx/my-frontend;
# Set cache expires from the map we defined.
expires $expires;
# Security. Don't send nginx version in Server header.
server_tokens off;
# Fallback to default language if no preference defined by browser
if ($accept_language ~ "^$") {
set $accept_language "en";
}
# Redirect "/" to Angular app in browser's preferred language
rewrite ^/$ /$accept_language permanent;
# Everything under the Angular app is always redirected to Angular in the correct language
location ~ ^/(en|de|ua) {
try_files $uri /$1/index.html?$args;
# Add security headers from separate file
# include /etc/nginx/security-headers.conf;
}
# Proxy for APIs.
# location /api {
# proxy_pass https://api.address.here;
# }
}
app.module.ts
import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { ServiceWorkerModule } from "@angular/service-worker";
import { ReactiveFormsModule } from "@angular/forms";
import { HttpClientModule } from "@angular/common/http";
import { StoreModule } from "@ngrx/store";
import { EffectsModule } from "@ngrx/effects";
import { StoreDevtoolsModule } from "@ngrx/store-devtools";
import { environment } from "@environments/environment";
import { AppComponent } from "./app.component";
import { LandingComponent } from "./features/landing/landing.component";
import { NotFoundComponent } from "./features/not-found/not-found.component";
import { CoreModule } from "./core/core.module";
import { AppRoutingModule } from "./app-routing.module";
@NgModule({
imports: [
BrowserModule,
HttpClientModule,
ReactiveFormsModule,
ServiceWorkerModule.register("ngsw-worker.js", {
enabled: environment.production,
// Register the ServiceWorker as soon as the app is stable
// or after 30 seconds (whichever comes first).
registrationStrategy: "registerWhenStable:30000"
}),
AppRoutingModule,
StoreModule.forRoot({}, {}),
StoreDevtoolsModule.instrument({
maxAge: 25,
logOnly: environment.production
}),
EffectsModule.forRoot([]),
CoreModule
],
declarations: [AppComponent, LandingComponent, NotFoundComponent],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
app.component.html
<div class="home">
<header>
<ul class="flex">
<li><a routerLink="/">Home</a></li>
<li><a routerLink="/auth/sign-in">Sign in</a></li>
<li><a routerLink="/auth/sign-up">Sign up</a></li>
<li><a routerLink="/tasks">Tasks</a></li>
<li><a routerLink="/chats">Chats</a></li>
<li><a routerLink="/projects">projects</a></li>
<li>
<button>logout</button>
</li>
</ul>
<ng-container *ngFor="let language of languageList">
<a href="/{{language.code}}/">
<button class="button">{{language.label}}</button>
</a></ng-container
>
</header>
<div>
<router-outlet></router-outlet>
</div>
</div>
Then commands to run the app
docker compose build
docker compose up -d