Mongoose documentation says that transform does not work for nested objects.

So, for example, if we want to remove "_id" from models with have schema like:
let TranslationSchema = new mongoose.Schema(
  {
    keyName: {
      type: String,
      required: true
    },
    translations: [
      {
        translation: {
          type: String,
          required: true
        },
        language: {
          type: mongoose.Schema.Types.ObjectId,
          ref: 'Language',
          required: true
        }
      }
    ]
  },
  {
    toJSON: {
      virtuals: true,
      versionKey: false,
      transform: function (doc, ret) {
        delete ret._id;
        return ret;
      }
    }
  }
);
Then the JSON'ed result of selection via this model will look like this:
[
  {
    "name": {
      "keyName": "country_ukraina",
      "translations": [
        {
          "_id": "5a53c9ea155f084cdc189763",
          "language": {
            "name": "English",
            "code": "en",
            "id": "5a53c9e555b9b64cce97c74e"
          },
          "translation": "Ukraine"
        }
      ],
      "id": "5a53c9ea155f084cdc189762"
    },
    "id": "5a53c9ea155f084cdc189764"
  }
]
What's wrong with this response? - I guess we do not want to see "_id" in objects nested into "translations" field.

Unfortunately the only way to change this behavior is to add specific transform logic for nested objects:
let TranslationSchema = new mongoose.Schema(
  {
    keyName: {
      type: String,
      required: true
    },
    translations: [
      {
        translation: {
          type: String,
          required: true
        },
        language: {
          type: mongoose.Schema.Types.ObjectId,
          ref: 'Language',
          required: true
        }
      }
    ]
  },
  {
    toJSON: {
      virtuals: true,
      versionKey: false,
      transform: function (doc, ret) {
        delete ret._id;
        ret.translations.forEach((translation) => {
          translation.id = translation._id;
          delete translation._id;
        });
      }
    }
  }
);
As you see we had to add specific logic for processing nested "translations" objects and to delete "_id" fields from them.
The result for the new version of schema will be following:
[
  {
    "name": {
      "keyName": "country_ukraina",
      "translations": [
        {
          "language": {
            "name": "English",
            "code": "en",
            "id": "5a53c9e555b9b64cce97c74e"
          },
          "translation": "Ukraine",
          "id": "5a53c9ea155f084cdc189763"
        }
      ],
      "id": "5a53c9ea155f084cdc189762"
    },
    "id": "5a53c9ea155f084cdc189764"
  }
]
As you see our nested objects are also transformed now, so we implemented such a dirty workaround for mongoose restriction.