question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Fail to i18n strings with parameters containing {}

See original GitHub issue

Hi,

The polyfill fails to render strings with parameters containing {}. Origin issue: https://github.com/Chocobozzz/PeerTube/issues/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

Issue Analytics

  • State:open
  • Created 5 years ago
  • Reactions:4
  • Comments:5

github_iconTop GitHub Comments

1reaction
skirilocommented, 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

1reaction
skirilocommented, 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

Read more comments on GitHub >

github_iconTop Results From Across the Web

Unable to translate i18n strings containing spaces/dots in their ...
We have faced a problem with text list field's allowed values: if keys of allowed values contain spaces (which is allowed by the...
Read more >
How to pass arguments in I18n.translate - Stack Overflow
You can pass the params after the key. I18n.translate('error.messages.greater_than_or_equal_to', count: 2).
Read more >
Rails Internationalization (I18n) API - Ruby on Rails Guides
This guide will walk you through the I18n API and contains a tutorial on how to ... means defining translated values for these...
Read more >
chrome.i18n - Chrome Developers
You need to put all of its user-visible strings into a file named messages.json . Each time you add a new locale, you...
Read more >
String resources | Android Developers
Formatting strings. If you need to format your strings, then you can do so by putting your format arguments in the string resource,...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found