ibitcy / eo-locale

🌏Internationalize React apps 👔Elegant lightweight library based on Internationalization API
https://eo-locale.netlify.com/
MIT License
348 stars 12 forks source link

No Error on missing Translation #96

Closed mi-roh closed 1 year ago

mi-roh commented 2 years ago

For the Company im working we use eo-locale in several React-UIs and are pleased – Thanks for the great Work.

In several situations I miss the option to disable the '[eo-locale] id missing ...' Error.

F.E. the API delivers an status-key and an fallback message. Each Region has its own (huge) set of status. Some of the status-keys should be translated, some not. In this case, using the fallback message is wanted. But it throws an Error that pollutes our monitoring...

I miss an option, to disable the Error or know if a id exists.

If there should be something implemented, I could create a pull request. I would like to know witch is the desired Version. I have several implementation approaches:

Would love to here if an implementation is wished and if I can help.

via parameter

extend getMessageById by a custom paramter. The Smallest Option.

  public getMessageById = (
    id: string,
    defaultMessage?: string,

// Optional Parameter
    ignoreMissing? boolean,

  ): Message | object | null => {
    if (!this.memo[id]) {
      let message: object | string | undefined = id
        .split('.')
        .reduce(
          (acc, current) => (acc ? (acc as any)[current] : undefined),
          this.messages,
        );

      if (typeof message !== 'string') {

// Wrap Error
        if (!ignoreMissing) {
          this.onError(new Error(`[eo-locale] id missing "${id}"`));
        }

        message = defaultMessage || id;
      }

      this.memo[id] = message;
    }

    return this.memo[id];
  };

via method

a dedicated Method would return a boolean value. Maybe the more intuitive version. Bitewise perhaps the biggest option.

  public hasMessage = (
    id: string,
  ): boolean => {
    if (!this.issetMemo[id]) {
      let message: object | string | undefined = id
        .split('.')
        .reduce(
          (acc, current) => (acc ? (acc as any)[current] : undefined),
          this.messages,
        );
      this.issetMemo[id] = typeof message === 'string';
    }

    return this.issetMemo[id];
  };

more customizable onError / dedicated onMissing

with a custom Missing Logger that receives some more information, a custom filter can be implemented or even more advanced messages could be implemented.

export class Translator {
  // ...

// Add separate logger for Missing - can be overwritten with custom logger
  public onMissing: MissingLogger = (id, {options, languages}) => {
    // downward compatible
    this.onError(new Error(`[eo-locale] id missing "${id}"`))
  }

  // ...

  public translate = (
    id: string,
    options: FormatMessageOptions = {},
  ): string => {

// Add options
    const message = this.getMessageById(id, options.defaultMessage, options);

    // ...

    return String(message);
  };

  public getMessageById = (
    id: string,
    defaultMessage?: string,

// Add option (only for Logging) - could be merged with / replace defaultMessage with working downward compatible
    _options: FormatMessageOptions = {},
  ): Message | object | null => {
      // ...

      if (typeof message !== 'string') {

        // Use onMissing
        this.onMissing(
          id,
          {
            options: _options,
            language: this.language,
          }
        );
        // ...
      }
    // ...
  };
}

export type ErrorLogger = (error: Error) => void;
export type MissingLogger = (id: string, {options: FormatMessageOptions, language: string, }

In the Project setting a custom onMissing with a custom option could filter the Error or do custom logging:


translator.onMissing = (id, {options, language}) => {
  if (!options.ignoreMissing) {
    console.warn(`[UI-Name] - missing translation for '${id}' in ${language}`)
  }
}

const text = translator.translate('unknown', {ignoreMissing: true, defaultMessage: 'fallback {param}', param: 'PARAM'})
// text: 'fallback PARAM'
pret-a-porter commented 2 years ago

Hi @mi-roh! Thanks for the issue. You can provide custom onError handler to TranslationsProvider component as property. By default it is console.error.

pret-a-porter commented 2 years ago

@mi-roh Does it work for you? Shall I close the issue?

mi-roh commented 2 years ago

Hi @pret-a-porter, not really. In my subjective view, onError is ideal to link eo-locale with your monitoring. But its not useful to prefilter messages.

We already use a custom onError for our monitoring. But there is no way to filter between the Errors passed. Beside of filter on the Error-Message-Shema and grabbing the missing id with an regex.

What I haven't mentioned in my first post. Within onError there is no way to detect if a default message was used. Even that little Information would optimize the filter options. f.e.: this.onError(new Error(`[eo-locale] id missing "${id}" in ${this.language}. Displayed "${message}" instead.`))

I created a fork with two commits. First adds a custom missing handler. To prevent regexing on error messages. Second passes the FormatMessageOptions down to the missing handler. Custom filters or logging messages are possible for there is now a context given.

With a wrapper and some regexing we get the job done. I don't know if I'm the only one how would like to prefilter. But I would be happy for every little improvement. Even if only something like Displayed "${message}" instead. gets added to the Error.