Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fail to i18n strings with parameters containing {} #35

Open
Chocobozzz opened this issue Jul 9, 2018 · 5 comments
Open

Fail to i18n strings with parameters containing {} #35

Chocobozzz opened this issue Jul 9, 2018 · 5 comments

Comments

@Chocobozzz
Copy link

Hi,

The polyfill fails to render strings with parameters containing {}. Origin issue: Chocobozzz/PeerTube#756

How to reproduce:

Add

  it("Should support parameters with special characters", () => {
    const i18nService = getService();
    expect(i18nService("This is a test {{ok}} !", {ok: "{hello}"})).toBe("Ceci est un test {hello} !");
  });

to i18n-polyfill.service.spec.ts test file.

Stacktrace:

  ● Polyfill › Should support parameters with special characters

    Unexpected character "EOF" (Do you have an unescaped "{" in your template? Use "{{ '{' }}") to escape it.) ("Ceci est un test {hello} ![ERROR ->]"): 0:26
    Invalid ICU message. Missing '}'. ("Ceci est un test {hello} ![ERROR ->]"): 0:26

      63 |     private i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {},
      64 |     public digest: (m: i18n.Message) => string,
    > 65 |     interpolationConfig: InterpolationConfig,
      66 |     missingTranslationStrategy: MissingTranslationStrategy,
      67 |     public mapperFactory?: (m: i18n.Message) => PlaceholderMapper,
      68 |     console?: Console
      
      at TranslationBundle.get (lib/src/parser/html.ts:65:19)
      at Visitor.translateMessage (lib/src/parser/html.ts:358:45)
      at Visitor.visitElement (lib/src/parser/html.ts:285:45)
      at Element.visit (lib/src/ast/ast.ts:68:24)
      at Visitor.merge (lib/src/parser/html.ts:228:37)
      at HtmlParser.mergeTranslations (lib/src/parser/html.ts:33:24)
      at I18n (lib/src/i18n-polyfill.ts:65:44)
      at it (test/i18n-polyfill.service.spec.ts:88:16)
      at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:392:26)
      at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/proxy.js:79:39)
      at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:391:32)
      at Zone.Object.<anonymous>.Zone.run (node_modules/zone.js/dist/zone.js:142:43)
      at Object.testBody.length (node_modules/jest-zone-patch/index.js:50:27

Is it up to us to escape parameters values?

Thanks

@skirilo
Copy link

skirilo commented Oct 10, 2018

Tried to escape curly braces. This didn't work for me. Escaping gives escaped symbol in the translation file. Removing escaping in the translation file gives an error. Just refused from using curly braces at all

@NagaSainath
Copy link

Can anyone tell me how can I use i18n outside the class like enums or const arrays something like .model.ts files?

@skirilo
Copy link

skirilo commented Jul 22, 2019

In order to use i18n outside of classes you need to initialize this statically before bootstrapping. This may look like including some translation manager with a static method which returns an instance of this translation service. Name it as i18n at the beginning of your .model.ts file

@NagaSainath
Copy link

@skirilo Do you have a example for me if you don't mind

@skirilo
Copy link

skirilo commented Jul 23, 2019

@NagaSainath I think this is a wrong thread for this discussion. But I'll give my answer here. Please move it to the correct thread if needed.

  1. You need a "translation service". Suppose you add it to a module with name "yourmodulewiththetranslationservice". And let's name it translation.service.ts:
import {MissingTranslationStrategy} from "@angular/core";
import {I18n, I18nDef} from "@ngx-translate/i18n-polyfill";
import {InjectionToken} from '@angular/core';

export const USE_TRANSLATION_SERVICE = new InjectionToken<boolean>('useTranslationService');
let translationService: I18n;

export const i18nServiceFactory = (useService: boolean = true) => {
  if(!translationService) {
      if(useService) {
          let baseLocation = "base location of your app";
          let loadingTranslationFailed = false;
          let translations = "";
          try {
              let request = new XMLHttpRequest();
              request.open('GET', baseLocation + '/locale/translations.xlf', false);  // `false` makes the request synchronous
              request.send(null);
              if (request.status === 200) {
                  translations = request.responseText;
              } else {
                  console.log("Translation file could not be loaded.");
              }
          } catch (e) {
              loadingTranslationFailed = true;
              console.log("Translation file could not be loaded.");
          }

          if(!loadingTranslationFailed) {
              translationService = new I18n("xlf", translations, 'en', MissingTranslationStrategy.Ignore);
              if(translations.length > 0)
                console.log('Translation service was initialized successfully');
              else
                  console.log('Default translation values will be used');
              return translationService;
          }
      }

      console.error('Translation service was not initialized properly. Dummy translation service will be used');
      translationService = (value: string | I18nDef, params: { [key: string]: any; }) => {
          if(typeof value == 'string')
            return value;
          else if(typeof value == 'object') //instanceof I18nDef
              return value.value;
          else
              'Unknown value type';
      }
  }
  return translationService;
};
  1. In your app.module.ts:
    In imports add
import { I18n } from '@ngx-translate/i18n-polyfill';
import { i18nServiceFactory, USE_TRANSLATION_SERVICE} from "@yourmodulewiththetranslationservice";

Add this code to the corresponding place below:

@NgModule({
// .... some code
providers: [
// ...
  {provide: USE_TRANSLATION_SERVICE, useValue:true},
  {provide: I18n, useFactory: i18nServiceFactory, deps:[USE_TRANSLATION_SERVICE]},
// ...
],
  1. In the file where you need to initialize some static content you need to add the following:
import {I18n} from "@ngx-translate/i18n-polyfill";
import {i18nServiceFactory} from "@yourmodulewiththetranslationservice";

const i18n: I18n = i18nServiceFactory();

Then use it like this:

export const SomeConstants = {
  STRCONSTANTNAME: i18n({value: 'Some text to translate', id: 'Constants_ConstantUniqueID'}),
}

And quite a lot of time passed since I used it. As far as I remember I altered the extraction tool as well to process nodes with 'i18n' name to have automatic extraction

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants