i18n-polyfill
i18n-polyfill copied to clipboard
Fail to i18n strings with parameters containing {}
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
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
Can anyone tell me how can I use i18n outside the class like enums or const arrays something like .model.ts files?
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
@skirilo Do you have a example for me if you don't mind
@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.
- 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;
};
- 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]},
// ...
],
- 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