Greentube / localize-router

An implementation of routes localisation for Angular
MIT License
193 stars 93 forks source link

Angular recognize only the default lang in SSR. #163

Open bcentdev opened 5 years ago

bcentdev commented 5 years ago

I'm submitting a ... (check one with "x")

[x] bug report => Search github for a similar issue or PR before submitting
[ ] feature request => Please check if similar feature request does not exist
[ ] support request => Suggested place for help and support is [stackoverflow](https://stackoverflow.com/), search for similar question before posting

Description

Hello, I have an Angular application with Angular Universal, localize-router and ngx-translate installed. When I navigate in the client side of the website, the languages are working well. I have the default language (es) configured to be hidden in the url and english as secondary language. For example:

es => http://localhost:4000/quotation :white_check_mark: en => http://localhost:4000/en/quotation :white_check_mark:

The problem is that if I deactivate Javascript and force to load the server side of the website, only It works the default language.

es => http://localhost:4000/quotation :white_check_mark: en => http://localhost:4000/en/quotation :x:

Even If I try to put the default language in the url, It works too:

es => http://localhost:4000/es/quotation :white_check_mark:

Node returns me this error in console:

Error: Cannot match any routes. URL Segment: 'en/quotation'

It's like the localize is not well configured but I think that I configured similar to the client side.

🔬 My files

Dependencies of package.json


{
  "dependencies": {
    "@angular/animations": "^7.2.10",
    "@angular/common": "^7.2.12",
    "@angular/compiler": "^7.2.12",
    "@angular/core": "^7.2.12",
    "@angular/forms": "^7.2.12",
    "@angular/http": "^7.2.12",
    "@angular/platform-browser": "^7.2.12",
    "@angular/platform-browser-dynamic": "^7.2.12",
    "@angular/platform-server": "^7.2.12",
    "@angular/router": "^7.2.12",
    "@bugsnag/js": "^6.0.0",
    "@bugsnag/plugin-angular": "^6.0.0",
    "@ng-bootstrap/ng-bootstrap": "^4.1.1",
    "@nguniversal/express-engine": "^7.1.1",
    "@nguniversal/module-map-ngfactory-loader": "^7.1.1",
    "@ngx-translate/core": "^11.0.1",
    "@ngx-translate/http-loader": "^4.0.0",
    "bootstrap": "^4.3.1",
    "classlist.js": "^1.1.20150312",
    "compression": "^1.7.4",
    "cookie-parser": "^1.4.4",
    "core-js": "^2.6.5",
    "express": "^4.16.4",
    "font-awesome": "^4.7.0",
    "jquery": "^3.3.1",
    "localize-router": "^2.0.0-RC.2",
    "localize-router-lazy-universal-module-loader": "^1.0.1",
    "material-design-icons": "^3.0.1",
    "ngx-cacheable": "^1.1.6",
    "ngx-cookie-service": "^2.1.0",
    "ngx-device-detector": "^1.3.5",
    "ngx-owl-carousel-o": "^1.1.1",
    "rxjs": "^6.4.0",
    "rxjs-compat": "^6.4.0",
    "standard-version": "^4.4.0",
    "tslib": "^1.9.0",
    "web-animations-js": "^2.3.1",
    "zone.js": "^0.8.29"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^0.13.8",
    "@angular/cli": "^7.3.8",
    "@angular/compiler-cli": "^7.2.12",
    "@angular/language-service": "^7.2.12",
    "@types/jasmine": "^2.8.16",
    "@types/jasminewd2": "~2.0.3",
    "@types/jquery": "^3.3.29",
    "@types/node": "^8.10.45",
    "angular-router-loader": "^0.8.5",
    "awesome-typescript-loader": "^5.2.1",
    "codelyzer": "~4.5.0",
    "jasmine-core": "~2.99.1",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~3.1.1",
    "karma-chrome-launcher": "~2.2.0",
    "karma-coverage-istanbul-reporter": "^2.0.5",
    "karma-jasmine": "~1.1.2",
    "karma-jasmine-html-reporter": "^0.2.2",
    "nucleus-styleguide": "^1.1.1",
    "protractor": "~5.4.0",
    "ts-loader": "^5.3.3",
    "ts-node": "~7.0.0",
    "tsconfig-paths-webpack-plugin": "^3.2.0",
    "tslint": "~5.11.0",
    "typescript": "~3.1.6",
    "webpack-cli": "^3.3.0"
  }
}

NgModule of app.module.ts


@NgModule({
    declarations: [
        AppComponent,
        CalendarModalComponent,
        TimeSlotModalComponent,
        DiscountModalComponent
    ],
    imports: [
        BrowserModule.withServerTransition({ appId: 'frontend' }),
        AppRoutingModule,
        BrowserAnimationsModule,
        CoreModule,
        NgbModule.forRoot(),
        SharedModule,
        TranslateModule.forRoot({
            loader: {
                provide: TranslateLoader,
                useFactory: (http: HttpClient) => {
                    return new TranslateHttpLoader(http);
                },
                deps: [HttpClient]
            }
        }),
        LocalizeRouterModule.forRoot(routes,{
            parser: {
                provide: LocalizeParser,
                useFactory: (translate, location, settings) =>
                    new ManualParserLoader(translate, location, 
                        new LocalizeRouterSettings(false, false, 'LocalStorage', 'LOCALIZE_DEFAULT_LANGUAGE', function(languages, cachedLang, browserLang) {
                            return languages[0];
                        }), config.languages, ''),
                deps: [TranslateService, Location, LocalizeRouterSettings]
            }
        })
    ],
    providers: [
        CookieService,
        { provide: ErrorHandler, useFactory: errorHandlerFactory },
    ],
    bootstrap: [AppComponent],
    entryComponents: [
        CalendarModalComponent,
        TimeSlotModalComponent,
        DiscountModalComponent
    ]
})

app.server.module.ts


import { Observable } from 'rxjs';
import { Location } from '@angular/common';
import { Routes } from '@angular/router';
import { LocalizeRouterSettings, LocalizeRouterModule, ManualParserLoader } from 'localize-router';
import { NgModule } from '@angular/core';
import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
import { LazyUniversalModuleLoaderProvider } from 'localize-router-lazy-universal-module-loader';

import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { TranslateService, TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { routes, AppRoutingModule } from './app-routing.module';
import { config } from '@config/config';

let fs = require('fs');

export class TranslateUniversalLoader implements TranslateLoader {
    /**
     * Gets the translations from the server
     * @param lang
     * @returns {any}
     */
    public getTranslation(lang: string): Observable {
        return Observable.create(observer => {
            observer.next(JSON.parse(fs.readFileSync(`../src/assets/i18n/${lang}.json`, 'utf8')));
            observer.complete();
        });
    }
}

export function translateLoaderFactory() {
    return new TranslateUniversalLoader();
}

export class LocalizeUniversalLoader extends ManualParserLoader {
    /**
     * Gets config from the server
     * @param routes
     */
    public load(routes: Routes): Promise {
        return new Promise((resolve: any) => {
            let data: any = JSON.parse(fs.readFileSync('../src/assets/locales.json', 'utf8'));
            this.locales = data.locales;
            this.prefix = '';
            this.init(routes).then(resolve);
        });
    }
}

export function localizeLoaderFactory(translate: TranslateService, location: Location, settings: LocalizeRouterSettings) {
    return new LocalizeUniversalLoader(translate, location, 
        new LocalizeRouterSettings(false, false, 'LocalStorage', 'LOCALIZE_DEFAULT_LANGUAGE', function(languages, cachedLang, browserLang) {
            return languages[0];
    }), config.languages, '');
}

@NgModule({
    imports: [
        AppModule,
        ServerModule,
        AppRoutingModule,
        ServerTransferStateModule,
        ModuleMapLoaderModule,
        TranslateModule.forRoot({
            loader: {
                provide: TranslateLoader,
                useFactory: translateLoaderFactory
            }
        }),
        LocalizeRouterModule.forRoot(routes,{
            parser: {
                provide: ManualParserLoader,
                useFactory: localizeLoaderFactory,
                deps: [TranslateService, Location, LocalizeRouterSettings]
            }
        })
    ],
    providers: [
        LazyUniversalModuleLoaderProvider
    ],
    bootstrap: [AppComponent],
})
export class AppServerModule { }

locales.json


{
    "locales": [
        "es",
        "en"
    ]
}

'config.languages' returns...


export const languages = [
    'es', 'en'
];

🌍 Your Environment

Angular Version:


Angular CLI: 7.3.8
Node: 11.13.0
OS: darwin x64
Angular: 7.2.14
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, platform-server, router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.13.8
@angular-devkit/build-angular     0.13.8
@angular-devkit/build-optimizer   0.13.8
@angular-devkit/build-webpack     0.13.8
@angular-devkit/core              7.3.8
@angular-devkit/schematics        7.3.8
@angular/cli                      7.3.8
@ngtools/webpack                  7.3.8
@schematics/angular               7.3.8
@schematics/update                0.13.8
rxjs                              6.5.1
typescript                        3.1.6
webpack                           4.29.0

Localize Router Version:


"localize-router": "^2.0.0-RC.2"

Anything else relevant? I have tried to do changes in LocalizeRouterSettings, change ManualParserLoader by LocalizeParser... But nothing.

I have asked to Melisandre, Geralt de Rivia and Gandalf... But nothing...

Who could be my Superman?

Thank you so much!

gilsdav commented 5 years ago

Hello,

Can you check if it works as expected when you enable alwaysSetPrefix ?

Only these urls have to works : es => http://localhost:4000/es/quotation en => http://localhost:4000/en/quotation

Thank's

bcentdev commented 5 years ago

It doesn't work. Anyway I need to configure it without prefix in the default language.

Thank you.

gilsdav commented 5 years ago

Hello, Can you check files app-routing.module.ts, app.module.ts and app.server.module.ts from https://github.com/gilsdav/angular-universal-localize-router/tree/master/src/app and unsure you have imports and providers at the same place and with the same order ?

For example LocalizeRouterModule on app-routing.modules.ts and imported after RouterModule

martinvano commented 4 years ago

any update on this issue? We are experiencing similar symptoms, very often CSR & SSR behavior is not consistent so I am trying to figure what's up before I open a new issue