1

我是 Angular 的新手,并且已经开始构建一个具有下一个特性的新项目。对不起,如果解释不够清楚。我很乐意提供更多信息,但不想通过我的完整项目获得不相关的信息。

我在用着

  • 视觉工作室 2015
  • ASPNET 核心
  • AspNetCore.Angular 服务
  • ngx-翻译

我已按照 [ https://github.com/ngx-translate/core][1]中提供的示例和说明进行操作

我的代码按预期工作,能够翻译 4 种语言并在路由器内的多个组件中使用管道指令。这意味着我的 json 文件正在加载并且结构正确。我看到了许多报告,其中 json 文件存在错误,但我的情况并非如此。

但是我的问题是我无法添加设置默认语言和要在构造函数中使用的语言的行。

import { Component } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';

@Component({
    selector: 'app',
    template: require('./app.component.html'),
    styles: [require('./app.component.css')]
})
export class AppComponent {
    constructor(private translate: TranslateService) {

        
        translate.addLangs(["en", "fr", "es", "de"]);
        //Error shows up when uncommenting any of the next two lines
        //translate.setDefaultLang('en');
        //translate.use('en');

    }
}

每当我取消注释这些行时,我都会收到错误

异常:调用节点模块失败并出现错误:SyntaxError:JSON.parse () 位置 0 处 JSON 中的意外令牌

如果我评论这些行,我的应用程序启动没有问题,唯一的细节是语言尚未设置。我可以在下拉列表中手动选择它,然后一切都按预期工作。

这是html文件

<div id="MainContainer" class='container-fluid'>
    <div class='row'>
        <label>
          {{ "SELECT" | translate }}<br />
          <select #langSelect (change)="translate.use(langSelect.value)">
            <option *ngFor="let lang of translate.getLangs()" [value]="lang" [selected]="lang === translate.currentLang">{{ lang }}</option>
          </select>
        </label>
    </div>
</div>

这是我的应用程序模块

import { NgModule }                 from '@angular/core';
import { UniversalModule }          from 'angular2-universal'; // this automatically imports BrowserModule, HttpModule, and JsonpModule too.
import { Http }                     from '@angular/http';
import { FormsModule }              from '@angular/forms'; // <-- ngModel lives here
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader }      from '@ngx-translate/http-loader';

// main Routing
import { AppRoutingModule }         from './routers/app-routing.module';


// own Services
import { ImgService }               from './services/img.service';

// own components
import { AppComponent }             from './components/app/app.component';
import { NavMenuComponent }         from './components/nav-menu/nav-menu.component';
import { CompleteSystemComponent }  from './components/complete-system/complete-system.component';
import { VehicleComponent }         from './components/vehicle/vehicle.component';
import { RobotComponent }           from './components/robot/robot.component';
import { MapComponent }             from './components/map/map.component';
import { StatusBarComponent }       from './components/status-bar/status-bar.component';
import { RobotCameraComponent }     from './components/robot-camera/robot-camera.component';
import { ImageProcessingComponent } from './components/image-processing/image-processing.component';
import { Station1Component }        from './components/station1/station1.component';
import { Station2Component }        from './components/station2/station2.component';


//AoT requires an exported function for factories
export function HttpLoaderFactory(http: Http) {
    //return new TranslateHttpLoader(http, '/i18n/', '.json');
    //return new TranslateHttpLoader(http, 'assets/i18n/', '.json');
    return new TranslateHttpLoader(http);
}


@NgModule({
    imports: [
        UniversalModule, // must be first import. This automatically imports BrowserModule, HttpModule, and JsonpModule too.
        FormsModule,
        AppRoutingModule,
        TranslateModule.forRoot({
            loader: {
                provide: TranslateLoader,
                useFactory: HttpLoaderFactory,
                deps: [Http]
            }
        }),

    ],
    declarations: [
        AppComponent,
        NavMenuComponent,
        RobotComponent,
        VehicleComponent,
        CompleteSystemComponent,
        MapComponent,
        StatusBarComponent,
        RobotCameraComponent,
        ImageProcessingComponent,
        Station1Component,
        Station2Component
    ],
    providers: [
        ImgService
    ],
    bootstrap: [AppComponent]
})
export class AppModule {
}

如果有人能给我一些尝试的提示,我将不胜感激。到目前为止,这些是我试图解决问题的事情

  • 将默认语言的定义和使用移动到 ngOnInit (同样失败)
  • 移动默认语言的定义并使用按钮调用的函数(它可以正常工作)
  • 尝试移动默认语言的定义并使用到 ngAfterViewInit() 和 ngAfterViewChecked()。

我怀疑问题是没有从 wwwroot 目录加载 json 文件,或者应用程序启动时服务无法在那里找到它们。应用程序启动后,它发现它们没有问题,一切都按预期工作。

再次感谢您的评论,如果更多信息有助于调试此问题,请告诉我

4

3 回答 3

1

我终于设法解决了这个问题的近 80%(我也是 angular 新手,希望看到更好的解决方案)。

  1. 将所有软件包更新到最新版本(对我来说它是 angular 4.3.4)。

  2. 更改为文档中的 HttpClient ( https://github.com/ngx-translate/core )

这是我的app.module.shared.ts文件:

import { NgModule,Inject } from '@angular/core';
import { RouterModule,Router } from '@angular/router';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { HttpModule } from '@angular/http';

import { HttpClientModule, HttpClient } from '@angular/common/http';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';

import { AppComponent } from './components/app/app.component'
import { NavMenuComponent } from './components/navmenu/navmenu.component';
import { HomeComponent } from './components/home/home.component';
import { FetchDataComponent } from './components/fetchdata/fetchdata.component';
import { CounterComponent } from './components/counter/counter.component';

export function HttpLoaderFactory(http: HttpClient) {
    return new TranslateHttpLoader(http);
}

export const sharedConfig: NgModule = {
    bootstrap: [ AppComponent ],
    declarations: [
        AppComponent,
        NavMenuComponent,
        CounterComponent,
        FetchDataComponent,
        HomeComponent,
    ],
    imports: [
        HttpClientModule,
        TranslateModule.forRoot({
            loader: {
                provide: TranslateLoader,
                useFactory: HttpLoaderFactory,
                deps: [HttpClient]
            }
        }),
        RouterModule.forRoot([
            { path: '', redirectTo: 'home', pathMatch: 'full' },
            { path: 'home', component: HomeComponent },
            { path: 'counter', component: CounterComponent },
            { path: 'fetch-data', component: FetchDataComponent },
            { path: '**', redirectTo: 'home' }
        ])
    ]
};

这是有趣的部分。唯一的问题是 ui 的翻译是在浏览器中完成的,这对 SEO 不利。如果我弄清楚了,我会更新。

app.component.ts文件:

import { Component, OnInit, Inject } from '@angular/core';

import { PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';

import { TranslateService } from '@ngx-translate/core';

@Component({
    selector: 'app',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
    isBrowser: boolean;


    constructor( @Inject(PLATFORM_ID) platformId: Object ,@Inject('ORIGIN_URL') originUrl: string, private translate: TranslateService) {
        this.isBrowser = isPlatformBrowser(platformId);

        if (this.isBrowser) {
            translate.setDefaultLang('en');
            translate.use('en');
        }

    }
    changeLang(lang: string) {
        this.translate.use(lang);
    }
}

现在解决了它,如果你想出一个不会错过 SEO 的更好的解决方案,分享它:)。

于 2017-08-11T17:22:42.857 回答
0

我遇到了同样的问题,我就是这样解决的:

1)提供词汇查看:

public async Task<IActionResult> Index()
{
    var serverData = new IndexViewModel
    {
        Vocabulary = await GetUserLanguage()
    };

    return View(serverData);
}

词汇:

public class Vocabulary
{
    public String Language { get; set; }

    public Object Translations { get; set; }
}

获取用户语言():

private async Task<Vocabulary> GetUserLanguage()
    {
        // Get lang form request headers
        StringValues langs;
        HttpContext.Request.Headers.TryGetValue("Accept-Language", out langs);

        if(langs.Count == 0)
        {
            return null;
        }

        var langsArr = langs.ToArray();

        for(var i = 0; i < langsArr.Length; i++)
        {
            var lang = langsArr[i];

            if (lang.Contains("-"))
            {
                langsArr[i] = lang.Split("-")[0];
            }

            if (lang.Contains("_"))
            {
                langsArr[i] = lang.Split("_")[0];
            }
        }

        langsArr = langsArr == null
            ? new String[] { "en" }
            : langsArr;

        Vocabulary vocabulary = null;

        foreach (var userLang in langsArr)
        {
            var path = $"{_hostingEnvironment.WebRootPath}/assets/i18n/{userLang}.json";
            FileInfo fileInfo = new FileInfo(path);

            if (fileInfo.Exists)
            {
                vocabulary = new Vocabulary
                {
                    Language = userLang,
                    Translations = await System.IO.File.ReadAllTextAsync(path)
                };

                break;
            }
        }

        return vocabulary;
    }

2) 将模型发送到 boot.server。

索引.cshtml:

<app asp-prerender-module="ClientApp/dist/main-server" asp-prerender-data="@JavaScriptHelper.Json(Model)">Loading...</app>

JavaScriptHelper.Json():

public static string Json(object obj)
    {
        JsonSerializerSettings settings = new JsonSerializerSettings
        {
            ContractResolver = new CamelCasePropertyNamesContractResolver(),
            Converters = new JsonConverter[]
            {
                new StringEnumConverter(),
            },
            StringEscapeHandling = StringEscapeHandling.EscapeHtml
        };

        return JsonConvert.SerializeObject(obj, settings);
    }

将数据从 MVC 传递到 Angular2 描述here

在 boot.server.ts 中,您可以通过 params.data 访问传递的服务器数据:// Imports

declare var global: any;

enableProdMode();

export default createServerRenderer(params => {
    let serverData = JSON.parse(params.data);
    serverData.vocabulary.translations = 
JSON.parse(serverData.vocabulary.translations);

global.serverData = serverData;

// ...

return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => {
    const appRef: ApplicationRef = moduleRef.injector.get(ApplicationRef);
    const state = moduleRef.injector.get(PlatformState);
    const zone = moduleRef.injector.get(NgZone);

return new Promise<RenderResult>((resolve, reject) => {
        zone.onError.subscribe((errorInfo: any) => reject(errorInfo));
        appRef.isStable.first(isStable => isStable).subscribe(() => {
            // Because 'onStable' fires before 'onError', we have to delay slightly before
            // completing the request in case there's an error to report
            setImmediate(() => {
                resolve({
                    html: state.renderToString(),

                    globals: {
                        serverData: serverData
                    }
                });
                moduleRef.destroy();
            });
        });
    });

对象global在节点环境中的预渲染期间可用。

globals: { serverData: serverData }过去的数据到窗口对象,在浏览器中可用

3) 在 app.component.ts 中设置默认语言: declare var serverData: any;

@Component({
    selector: 'app',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {

constructor(private translate: TranslateService, private http: Http, globalService: GlobalService, @Inject(PLATFORM_ID) platformId: Object) {
    let isBrowser = isPlatformBrowser(platformId);

    let lang = '';
    let translations = { };

    if (!isBrowser) {
        lang = serverData.vocabulary.language;
        translations = serverData.vocabulary.translations;
    } else {
        lang = (window as any).serverData.vocabulary.language;
        translations = (window as any).serverData.vocabulary.translations;
    }

    translate.setTranslation(lang, translations);
    translate.setDefaultLang(lang);
    translate.use(lang);
    }
}
于 2017-09-18T15:03:42.507 回答
0

<div id="MainContainer" class='container-fluid'>
    <div class='row'>
        <label>
          {{ "SELECT" | translate }}<br />
          <select #langSelect (change)="translate.use(langSelect.value)">
            <option *ngFor="let lang of translate.getLangs()" [value]="lang" [selected]="lang === translate.currentLang">{{ lang }}</option>
          </select>
        </label>
    </div>
</div>

于 2020-12-22T15:48:21.303 回答