Error handling and i18n?

Hi,
What is the recommendation regarding the translation of meteor errors?
I’m currently trying to localize errors returned by the accounts-password package but this is certainly a broader issue.

When Meteor errors are returned (say user already exists), how are we supposed to report them to the user using the client locale?
To use an i18n package, a key is usually required. What is the key in that case?
If the answer is the ‘reason’, here is an example with a ‘variable reason’ that makes it worse:
throw new Meteor.Error(403, displayName + " already exists.");

To other developers: How did you solved this in your projects ? Is there some error reason strings/key mapping package (did not see one so far)?
To MDG: is that already identified (did not find an issue though) and planned to be improved/fixed? What about taking that into account in new developments (a guideline for package developers will certainly be required too)?

Thanks.

I’m using universe:i18n for i18n. At the time when I was getting into React, tap:i18n was not compatible, so I went with universe.

So I have a /i18n folder that has different languages in it, in YAML format, e.g.:

general:
  cancelBtn: "Cancel"
  deleteBtn: "Delete"
  editBtn: "Edit"
  noBtn: "No"
  okBtn: "OK"
  yesBtn: "Yes"

error:
  alreadyExists: " already exists."

And these can be accessed like so:

import { _i18n as i18n } from 'meteor/universe:i18n';

// ... inside a component's render method:
render() {
  return <div>
    {someName + i18n.__('error.alreadyExists')}
  </div>
}

Yeah, my point is not about using a specific i18n package (I’m quite fine with gadicc/meteor-messageformat) but about making sure such packages will be usable, esp. with Meteor core features. I think the author of Meteor.Error had (partially though) the issue in mind but this got lost in the process.

Look for example at this code: https://github.com/meteor/meteor/blob/release-1.3.2.4/packages/accounts-base/accounts_server.js and look for ‘Error’…
Do you see anything that looks like a usable key ??

You will certain certainly use the message string (‘reason’ which is optional btw) as the key but this is not correct.
This is what softwarerero/meteor-accounts-t9n does… but I think this is only a workaround and only shows Meteor Error has not been properly used: the first field (currently 403 most of the time in the file above) should be used for defining (and documenting!) properly translatable error key codes.

To take your YAML example, will you write?

error:
        "You've been logged out by the server. Please log in again.": 
               "Some translation for You've been logged out by the server. Please log in again."

What about error strings that include a variable (username, email…)? Is that acceptable to parse/hack the string to extract some usable key?

With universe:i18n, you can definitely pass in variables. E.g.:

// In your React component render() method:
<div>{i18n.__('deleteError', { filename: this.props.filename })}</div>

(in YAML file):

deleteError: >
  An error occurred while deleting {$filename}. Try again later.
1 Like

And if you are calling an API (e.g. from the accounts package) that only returns an error with a reason text like:

An error occurred while deleting /tmp/seriously.txt. Try again later.

How do you call your i18n package to localize this text?

1 Like

I’m not exactly sure what you’re asking…

May I ask how did you finally manage to do what you wanted?

Resurrecting this very old thread as it raises some valid questions which I believe have not been answered.

When you get a very detailed error message back from a core Meteor package. How are you supposed to intercept it and translate the error message?

if (error.message.includes("Email already exists") {
  error.message = translate(error.id); // How? Hard-coding the string?
}

How do I know which translation I need to give here? I can manually create a list of if-else statements, but that feels hacky as mentioned above. Some error messages include variables in the middle of them, so intercepting them becomes even worse, as you’re not sure that another error exists that contains, startsWith or endsWith a particular piece of text.

And on the client side, is there a way to intercept all DDP error messages and change the payload to be translated? We can easily translate our own custom errors on the server, but these built-in meteor errors are a pain.

Here’s an excerpt from one of the i18n files I have:

accounts:
  #---- accounts-base
  "Email already exists": "Die E-Mail Adresse wird bereits verwendet."
  "Email doesn't match the criteria": "E-Mail Adresse erfüllt die Anforderungen nicht."
  "Invalid login token": "Ungültiger Login-Token"
  "Login forbidden": "Anmeldedaten ungültig"
...
  "You've been logged out by the server Please log in again": "Die Sitzung ist abgelaufen, eine neue Anmeldung ist nötig."
...

Pay attention to the the last entry: I have removed the “.” from the end of the first sentence, otherwise the key would have become invalid. Consequently, I use a function to remove the “.” characters from the received error messages too, before I use them as a key in i18n.

Admittedly this all borders on the ridiculous. I only do this because I’m left with nothing else. One of the cornerstones of error handling is to NEVER EVER use messages in the error/exception thrown, for the exact same reason we see here. Ironically, using a unique string id as the first parameter in Meteor.Error is what’s recommended in the Meteor documentation – so far so good.

The second parameter however is not right in my opinion:

reason String
Optional. A short human-readable summary of the error, like ‘Not Found’.

The second parameter should be a context object with all relevant data that helps fixing what’s broken – and never a human-readable text.

The third parameter is also not right:

details String
Optional. Additional information about the error, like a textual stack trace.

In praxis, and for some unknown reason this must not be an object if the error is to be serialized (unless I’m mistaken). This should normally be a cause of type error, so that exception chains can be represented. Unfortunately the serialization and reproduction of javascript Error objects seems to be a huge problem.

1 Like