Пользовательская библиотека Angular с ngx-translate

Я создал свою собственную частную библиотеку angular с несколькими компонентами, чтобы облегчить себе жизнь. Я создал эту библиотеку, следуя этому руководству. Все получилось отлично, я даже добавил это в другой проект, чтобы проверить.

Теперь наступает момент, когда я должен поддерживать несколько языков, потому что я живу в Бельгии ????. Раньше я без проблем пользовался пакетом ngx-translate, поэтому подумал, что это будет легко.

Я добавил в свой проект папку assets / i18n с переводом json.  введите описание изображения здесь

Я обнаружил, что angular cli не включает активы в сборку, поэтому вам нужно добавить их вручную или с помощью gulp в папку dist перед созданием пакета. После этого я заметил, что метки не переводились, если я не включил перевод в файлы json основного приложения.

Каждый компонент в моей библиотеке имеет свой собственный модуль, поэтому я подумал, что мне просто нужно добавить модуль перевода в эти модули. Итак, я сделал следующее.

export function httpLoaderFactory(http: HttpClient) {
    return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}

@NgModule({
    imports: [
        CommonModule,
        FormsModule,
        DirectivesModule,
        ButtonModule,
        IconModule,
        PillModule,
        TranslateModule.forChild({
            loader: {
                provide: TranslateLoader,
                useFactory: (httpLoaderFactory),
                deps: [HttpClient]
            },
            isolate: true
        })
    ],
    declarations: [
        FilterComponent
    ],
    exports: [
        FilterComponent
    ]
})

Это усугубило ситуацию: не только этикетки все еще не переводились, но и мои основные приложения даже не использовали свои собственные переводы. У основного приложения не было проблем с переводами до тех изменений в модулях библиотеки ...

Так что да, вы догадались, я застрял ... Кажется, я не могу найти правильного решения.


person Beejee    schedule 29.12.2018    source источник
comment
Привет! Я понимаю вашу проблему. Я сомневаюсь, что перевод будет непосредственно в вашем библиотечном компоненте. Вы становитесь зависимыми от обновления вашей библиотеки. Можно ли передать переведенные слова / предложения как @Input?   -  person Wandrille    schedule 29.12.2018
comment
Я знаю, что вы имеете в виду, но переводы предназначены для меток aria, заголовков в тегах привязки (видны только при наведении курсора). Кроме того, эта библиотека предназначена только для личного использования, поэтому мне нужна самоподдерживающаяся библиотека, которая просто работает из коробки.   -  person Beejee    schedule 30.12.2018


Ответы (4)


Поскольку я ничего не добился, я попробовал другой подход, описанный в этом сообщении . Поэтому я преобразовал свои файлы json в файлы ts, возвращающие объект json. Затем я создал свою собственную службу translateService, которая добавляет переводы поверх уже существующих (добавленных json-файлами основного приложения), как описано в сообщении.

Это сработало, но предыдущие переводы были отменены или даже загружены слишком поздно. Это привело к тому, что приложение просто показывало ключи перевода вместо перевода. Поэтому вместо инициализации переводов, как в посте, я использовал подписку, чтобы сначала дождаться основных переводов.

//Library
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { en } from "../localization/en";
import { nl } from "../localization/nl";

@Injectable()
export class TranslateUiService {
    private availableLanguages = { en, nl };

    constructor(private translateService: TranslateService) {
    }

    public init(language: string = null): any {
        if (language) {
            //initialize one specific language
            this.translateService.setTranslation(language, this.availableLanguages[language], true);
        } else {
            //initialize all
            Object.keys(this.availableLanguages).forEach((language) => {
                this.translateService.setTranslation(language, this.availableLanguages[language], true);
            });
        }
    }
}

//App
constructor(private translateUiService: TranslateUiService, private translateService: TranslateService) {
        this.translateService.setDefaultLang('en');
        this.translateService.use('en').subscribe(() => {
            this.translateUiService.init('en');
        });
    }

person Beejee    schedule 30.12.2018
comment
this.translateService.setTranslation (...) с третьим параметром merge = true был уловкой. Модуль-config isolated = true у меня вообще не работал. И слияние было следующим лучшим решением для меня. - person Harald Brabenetz; 23.03.2020
comment
Этот подход работает не так, как ожидалось - по крайней мере, для меня. Языки объединены и поэтому не изолированы. Если библиотека содержит ключ перевода hello, а проект, использующий его, имеет тот же ключ перевода, один из этих переводов будет отменен. - person Sébastien BATEZAT; 26.06.2020
comment
Вы должны добавить префикс к своим ключам пакета, чтобы предотвратить это. - person Beejee; 29.06.2020

Я сделал это так же, как и Beejee, но расширил услуги перевода. В расширении TranslateService я добавляю перевод библиотеки на подуровень (ui. [Language]) переводов глобального приложения, поскольку мы используем тот же экземпляр, что и корневое приложение, и мы не хотим переопределять переводы корневого приложения. . Затем я предоставил расширение вместо обычного TranslateService на уровне компонента, поэтому оно используется даже для директивы translate в этом компоненте и является изолированным, что означает, что мы не уничтожаем перевод корневого приложения, переопределяя методы получения для currentLang и defautlLang.

ui-translate.service.ts:


const availableLanguages: any = {
  'de' : { ... YOUR DE TRANSLATIONS ... },
  'en' : { ... YOUR EN TRANSLATIONS ... }
  ...
};
const langPrefix = 'ui';

@Injectable({
  providedIn: 'root'
})
export class UiTranslateService extends TranslateService implements TranslateService {


  constructor(public store: TranslateStore,
              public currentLoader: TranslateLoader,
              public compiler: TranslateCompiler,
              public parser: TranslateParser,
              public missingTranslationHandler: MissingTranslationHandler,
              @Inject(USE_DEFAULT_LANG) useDefaultLang: boolean = true,
              @Inject(USE_STORE) isolate: boolean = false) {
    super(store, currentLoader, compiler, parser, missingTranslationHandler, useDefaultLang, isolate);

    this.onTranslationChange.subscribe((_res: TranslationChangeEvent) => {
      // ensure after translation change we (re-)add our translations
      if (_res.lang.indexOf(langPrefix) !== 0) {
        this.applyUiTranslations();
      }
    });

    this.applyUiTranslations();
  }

  private applyUiTranslations() {
    for (var lang in availableLanguages) {
      if (availableLanguages.hasOwnProperty(lang)) {
        this.setTranslation(langPrefix + '.' + lang, availableLanguages[lang], true);
      }
    }
  }

  /**
   * The default lang to fallback when translations are missing on the current lang
   */
  get defaultLang(): string {
    return langPrefix + '.' + this.store.defaultLang;
  }

  /**
   * The lang currently used
   */
  get currentLang(): string {
    return this.store.currentLang === undefined ? undefined : langPrefix + '.' + this.store.currentLang;
  }

  public getParsedResult(translations: any, key: any, interpolateParams?: Object): any {
    // apply our translations here
    var lang = (this.currentLang || this.defaultLang).split('.')[1];
    translations = lang == 'de' ? de : en;
    return super.getParsedResult(translations, key, interpolateParams);
  }
  public get(key: string | Array<string>, interpolateParams?: Object): Observable<string | any> {
    return super.get(key, interpolateParams);
  }
}

my.component.ts:

    @Component({
        selector: 'xxx',
        template: 'xxx',
        providers: [
            { provide: TranslateService, useClass: UiTranslateService }
        ]
    })
    export class MyComponent implements OnInit { }

my.module.ts:

    @NgModule({
        imports: [
            CommonModule,
            TranslateModule
        ]
     })
     export class ComponentsModule {}
person Jan Tchärmän    schedule 03.07.2019

Я подозреваю, что ваш шаг сборки перезаписывает en.json вашего основного приложения и т. Д. Файлом библиотеки. Следовательно, отсутствуют переводы основного приложения.

Одна вещь, которую вы могли бы рассмотреть вместо опции изоляции (которая потребует большего количества запросов к серверу для загрузки переводов), - это изменить шаг сборки, чтобы объединить файлы в вашей библиотеке с файлами переводов основного приложения.

Я бы порекомендовал каким-то образом использовать пространство имен для ключей перевода библиотеки, чтобы избежать возможных коллизий.

Тогда в библиотеке вы можете просто использовать TranslateModule.forChild()

В качестве альтернативы, если вы хотите сохранить изоляцию, попробуйте поместить файлы перевода библиотеки в подкаталог i18n и при необходимости изменить httpLoaderFactory

person Michael    schedule 29.12.2018

Вы можете сделать что-то подобное.

<div (click)="switchLanguage('en')"></div>

switchLanguage(language: string) {
        this.selectedLanguage = language
    }

В другом компоненте просто напишите в HTML что-то вроде этого.

<button><i class="fa fa-history"></i>{{'general.#time'|translate}}</button>

Это en.json

"general": {
        "#time": "Time"
}
person Abedin.Zhuniqi    schedule 29.12.2018
comment
Я знаю, как использовать его в обычном приложении, но он находится в библиотеке, которая будет импортирована в приложение. - person Beejee; 29.12.2018
comment
Для создания вашей библиотеки я не могу сказать, что вам нужно, но показывает ли она вам ошибки или вы можете сделать несколько журналов консоли, потому что это немного сложно достичь. - person Abedin.Zhuniqi; 29.12.2018