Issue
live_toast can cause an unrecoverable crash from the server (SystemLimitError) when called with too many variable messages.
Example:
LiveToast.send_toast(:error, "Failed to send email to #{email}, got error #{error}")
There is no way that LiveToast can correctly translate this without the broader context.
Current implementation will call dgettext with an ever changing message, each creating an atom, eventually filling the atom table.
Solutions
In my opinion, it is not the duty of LiveToast to do translations. It's not in it's job description, it doesn't have the context, it should not even assume the app uses Gettext and the caller should do the translation however she sees fit before passing the translated message. Also, in the current way, the error messages won't extract automatically as nothing is done at compile time, which makes it harder to keep the translation tables up to date.
In my mind, the very best solution is for LiveToast to remove this "feature" altogether (in a major release).
Assuming the authors are very attached to including translations, one solution is to tweak the API to allow for message_params and title_params options, so the user can pass "Failed to send email to %{email}, got error %{error}", message_params: [email: email, error: error]. This will create a single atom for the message template and avoid the issue, Personally, I find this solution ugly. It's still not explicit, the messages to translate will still not be extracted automatically, and the caller can still make an error unknowingly. Imagine if she's not even using translations herself and has a unilingual app...
An alternative solutions would be to have a setting and/or an option that allows no translation of messages (nor title). Note that if it's just a setting, the end users must make the right choice from the start, otherwise they'll have to go over all their call points to add translation later to be able to change the setting globally.
Instead of an additional setting, LiveToast could ship with a trivial NoTranslate endpoint (or include an example in the doc) and mention it in the doc, along with the potential issue of using translations, but that seems ugliest. I'm including this as it is currently the only way out for current users of LiveToast (short of forking):
defmodule MyApp.Helpers.NoTranslate do
@moduledoc """
A no-op Gettext backend for LiveToast that returns messages unchanged.
LiveToast unconditionally calls Gettext.dgettext() on all messages, which causes
atom exhaustion when messages contain %{} interpolation patterns. This module
bypasses translation entirely, returning messages as-is.
We handle translation ourselves before passing messages to LiveToast.
"""
@doc """
Returns the message unchanged without any translation.
This prevents Gettext from parsing interpolation patterns and creating atoms.
"""
def dgettext(_domain, message, _bindings \\ %{}) do
message
end
@doc """
Implement __gettext__ to satisfy Gettext.Backend behaviour.
LiveToast calls this to get the default locale.
"""
def __gettext__(:default_locale), do: "en"
def __gettext__(:interpolation), do: Gettext.Interpolation.Default
# Implement all the Gettext backend functions that LiveToast might call
def lgettext(_locale, _domain, _msgctxt, msgid, _bindings) do
{:ok, msgid}
end
def ldgettext(_locale, _domain, msgid, _bindings) do
{:ok, msgid}
end
def ldpgettext(_locale, _domain, _msgctxt, msgid, _bindings) do
{:ok, msgid}
end
def dpgettext(_domain, _msgctxt, msgid, _bindings \\ %{}) do
{:ok, msgid}
end
def gettext(msgid, _bindings \\ %{}) do
{:ok, msgid}
end
def pgettext(_msgctxt, msgid, _bindings \\ %{}) do
{:ok, msgid}
end
def ngettext(msgid, _msgid_plural, _n, _bindings \\ %{}) do
{:ok, msgid}
end
def dngettext(_domain, msgid, _msgid_plural, _n, _bindings \\ %{}) do
{:ok, msgid}
end
def pngettext(_msgctxt, msgid, _msgid_plural, _n, _bindings \\ %{}) do
{:ok, msgid}
end
def dpngettext(_domain, _msgctxt, msgid, _msgid_plural, _n, _bindings \\ %{}) do
{:ok, msgid}
end
end
Issue
live_toastcan cause an unrecoverable crash from the server (SystemLimitError) when called with too many variable messages.Example:
There is no way that
LiveToastcan correctly translate this without the broader context.Current implementation will call
dgettextwith an ever changingmessage, each creating an atom, eventually filling the atom table.Solutions
In my opinion, it is not the duty of
LiveToastto do translations. It's not in it's job description, it doesn't have the context, it should not even assume the app usesGettextand the caller should do the translation however she sees fit before passing the translated message. Also, in the current way, the error messages won't extract automatically as nothing is done at compile time, which makes it harder to keep the translation tables up to date.In my mind, the very best solution is for
LiveToastto remove this "feature" altogether (in a major release).Assuming the authors are very attached to including translations, one solution is to tweak the API to allow for
message_paramsandtitle_paramsoptions, so the user can pass"Failed to send email to %{email}, got error %{error}", message_params: [email: email, error: error]. This will create a single atom for the message template and avoid the issue, Personally, I find this solution ugly. It's still not explicit, the messages to translate will still not be extracted automatically, and the caller can still make an error unknowingly. Imagine if she's not even using translations herself and has a unilingual app...An alternative solutions would be to have a setting and/or an option that allows no translation of messages (nor title). Note that if it's just a setting, the end users must make the right choice from the start, otherwise they'll have to go over all their call points to add translation later to be able to change the setting globally.
Instead of an additional setting,
LiveToastcould ship with a trivialNoTranslateendpoint (or include an example in the doc) and mention it in the doc, along with the potential issue of using translations, but that seems ugliest. I'm including this as it is currently the only way out for current users ofLiveToast(short of forking):