Core: empty translation should show original string

Created on 8 Sep 2017  路  13Comments  路  Source: ngx-translate/core

I'm submitting a ... (check one with "x")

[ ] bug report => check the FAQ and search github for a similar issue or PR before submitting
[ ] support request => check the FAQ and search github for a similar issue before submitting
[x] feature request

Current behavior
"HELLO": ""
shows blank i the view

Expected/desired behavior
I expect it to show HELLO
this way translators can see which strings they need to translate

Reproduction of the problem
If the current behavior is a bug or you can illustrate your feature request better with an example, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar. You can use this template as a starting point: http://plnkr.co/edit/tpl:WccVZSBM0rUgq2sXSUbe

What is the expected behavior?

What is the motivation / use case for changing the behavior?

Please tell us about your environment:

"@ngx-translate/core": "^7.1.0",
"@ngx-translate/http-loader": "^1.0.1",

  • Angular version: 2.x.x

  • Browser: [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]

Most helpful comment

You can use this modified loader code. It removes empty strings from the files:

import {TranslateLoader} from '@ngx-translate/core';
import {HttpClient} from "@angular/common/http";
import 'rxjs/add/operator/map';

export class PruningTranslationLoader implements TranslateLoader {
    constructor(private http: HttpClient, private prefix: string = '/assets/i18n/', private suffix: string = '.json') {
    }

    public getTranslation(lang: string): any {
        return this.http.get(`${this.prefix}${lang}${this.suffix}`)
            .map((res: Object) => this.process(res));
    }

    private process(object: any) {
        const newObject = {};

        for (const key in object) {
            if (object.hasOwnProperty(key)) {
                if (typeof object[key] === 'object') {
                    newObject[key] = this.process(object[key]);
                }
                else if ((typeof object[key] === 'string') && (object[key] === '')) {
                    // do not copy empty strings
                }
                else {
                    newObject[key] = object[key];
                }
            }
        }

        return newObject;
    }
}

All 13 comments

A workaround for this is to exclude default language from extraction, i.e.:

    export class AppComponent {
      constructor(translate: TranslateService) {
        translate.setDefaultLang('none');
        translate.use('pl');
      }
    }

and in package.json:

"extract": "ngx-translate-extract --input ./src --output ./src/assets/i18n/{en,pl,de,fr}.json --clean --sort --format namespaced-json"

This way you will see untranslated strings from source instead of empty replacements.

I don't think this very special use case should result in the default being changed. An empty string is a valid value and should not be treated the same as null (which is the correct value for "not translated").

You could write a parser plugin which replaces empty strings with null/undefined, or something like that.

I agree with @gemal this is completely killing the fallback feature!

I you don't want it as standard feature ok, but anyway we should have the choice to filter out empty string.

You can use this modified loader code. It removes empty strings from the files:

import {TranslateLoader} from '@ngx-translate/core';
import {HttpClient} from "@angular/common/http";
import 'rxjs/add/operator/map';

export class PruningTranslationLoader implements TranslateLoader {
    constructor(private http: HttpClient, private prefix: string = '/assets/i18n/', private suffix: string = '.json') {
    }

    public getTranslation(lang: string): any {
        return this.http.get(`${this.prefix}${lang}${this.suffix}`)
            .map((res: Object) => this.process(res));
    }

    private process(object: any) {
        const newObject = {};

        for (const key in object) {
            if (object.hasOwnProperty(key)) {
                if (typeof object[key] === 'object') {
                    newObject[key] = this.process(object[key]);
                }
                else if ((typeof object[key] === 'string') && (object[key] === '')) {
                    // do not copy empty strings
                }
                else {
                    newObject[key] = object[key];
                }
            }
        }

        return newObject;
    }
}

@CodeAndWeb
I added the same code provided above and then added

providers: [
        {
          provide: TranslateLoader,
          useClass: PruningTranslationLoader,
        },
],

got this error

Error: Can't resolve all parameters for PruningTranslationLoader: (?, ?, ?).

I don't know why that doesn't work, but this way it should definitely work:
``` .ts
export function createTranslationLoader(http: HttpClient) {
return new PruningTranslationLoader(http); // specify more arguments if you want
}

and then
``` .ts
providers: [
        {
          provide: TranslateLoader,
          useFactory: (createTranslationLoader),
          deps: [HttpClient],
        },
],

(We're doing this for TranslateLoader.)

@ffdybuster
But I want to solve the missing translation issue.
This does not solve the missing translation issue.
before I have done in this way.

export function createTranslateLoader(http: HttpClient) {
    return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
providers: [
        {
          provide: TranslateLoader,
          useFactory: (createTranslationLoader),
          deps: [HttpClient],
        },
],

I have translation like this

// en-US.json file
"Close": "Close",
"Get Started": "Get Started"
// da-DK.json file
"Close": "",
"Get Started": ""

this does not show the value for Get Started and Close when i choose da-DK as language.
I want it to show the key if value is empty

I just told you how to use the piece of code @CodeAndWeb pasted, since you had trouble with that. In case that code doesn't work for you, you could try debugging it to find out what the problem is.

The loader code I provided here removes the empty translations. ngx-translate now tries to use the translations from the default language (e.g translate.setDefaultLang('en');) - if this is not found you should get {Close}.

See also #830

this is exactly what I needed, thanks @CodeAndWeb

just sharing more functional version of process function for those who are interested

function process(obj) {
  return Object.keys(obj)
    .filter(key => obj.hasOwnProperty(key) && obj[key] !== '')
    .reduce((res, key) => (res[key] = typeof obj[key] === 'object' ? process(obj[key]) : obj[key], res), {});
}

Using the code from above comments, following is more cleaner example.

Create a new file /src/app/pruning-loader.ts

import { HttpClient } from '@angular/common/http';
import { TranslateLoader } from '@ngx-translate/core';
import { map } from 'rxjs/operators';

export class PruningTranslationLoader implements TranslateLoader {

  constructor(
    private http: HttpClient,
    private prefix: string = '/assets/i18n/',
    private suffix: string = '.json') {
  }

  public getTranslation(lang: string): any {
    return this.http.get(`${this.prefix}${lang}${this.suffix}`)
    .pipe(map(
      (result: object) => this.process(result)
    ));
  }

  private process(object: object) {
    return Object.keys(object)
      .filter(key => object.hasOwnProperty(key) && object[key] !== '')
      .reduce((result, key) => (result[key] = typeof object[key] === 'object' ? this.process(object[key]) : object[key], result), {});
  }

}

And then in your app.module.ts file, change the following line

import { PruningTranslationLoader } from './pruning-loader';
...

// required for AOT compilation
export function HttpLoaderFactory(http: HttpClient) {
  return new PruningTranslationLoader(http);     // <--- change here
}

Assuming you have already configured your module imports like this

imports: [
  TranslateModule.forRoot({
    loader: {
      provide: TranslateLoader,
      useFactory: HttpLoaderFactory,
      deps: [HttpClient]
    }
  })
  ...
]

I guess you can just do:

export class CustomHandler implements TranslocoMissingHandler {
  handle(key: string, config: TranslocoConfig) {
    return key; // Returning original string
  }
}

And in your app.module.ts

{
  provide: TRANSLOCO_MISSING_HANDLER,
  useClass: CustomHandler
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

IterationCorp picture IterationCorp  路  3Comments

madoublet picture madoublet  路  3Comments

webprofusion-chrisc picture webprofusion-chrisc  路  4Comments

bjornharvold picture bjornharvold  路  3Comments

guysan picture guysan  路  4Comments