Skip to content

Commit

Permalink
Allow extraction to work by setting attribute at expansion time
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed Aug 20, 2024
1 parent 903a466 commit 539b72c
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 63 deletions.
17 changes: 13 additions & 4 deletions lib/gettext.ex
Original file line number Diff line number Diff line change
Expand Up @@ -619,10 +619,19 @@ defmodule Gettext do

case Keyword.keyword?(opts) && Keyword.fetch(opts, :backend) do
{:ok, backend} ->
quote do
Module.register_attribute(__MODULE__, :__gettext_backend__, persist: true)
@__gettext_backend__ unquote(backend)
import Gettext.Macros
case Macro.expand(backend, __CALLER__) do
backend when is_atom(backend) and backend not in [nil, false, true] ->
# We need to store the module backend at expansion time because of extraction
Module.put_attribute(__CALLER__.module, :__gettext_backend__, backend)

quote do
import Gettext.Macros
end

_ ->
raise ArgumentError,
"the :backend option on \"use Gettext\" expects the backend " <>
"to be a literal atom/alias/module, got: #{Macro.to_string(backend)}"
end

_other ->
Expand Down
23 changes: 9 additions & 14 deletions lib/gettext/macros.ex
Original file line number Diff line number Diff line change
Expand Up @@ -678,25 +678,20 @@ defmodule Gettext.Macros do
defp expand_domain(domain, env), do: expand_to_binary(domain, "domain", env)

defp backend(%Macro.Env{} = env) do
if Module.open?(env.module) do
Module.get_attribute(env.module, :__gettext_backend__) ||
raise "expected to find non-nil @__gettext_backend__ attribute in #{inspect(env.module)}"
else
List.first(env.module.__info__(:attributes)[:__gettext_backend__]) ||
raise "expected to find non-nil @__gettext_backend__ attribute in #{inspect(env.module)}"
end
Module.get_attribute(env.module, :__gettext_backend__) ||
raise """
in order to use Gettext.Macros, you must:
use Gettext, backend: ...
"""
end

defp expand_to_binary(term, what, %Macro.Env{} = env)
when what in ~w(domain msgctxt msgid msgid_plural comment) do
gettext_module =
if Module.open?(env.module) do
Module.get_attribute(env.module, :__gettext_backend__)
else
List.first(env.module.__info__(:attributes)[:__gettext_backend__])
end

raiser = fn term ->
gettext_module = Module.get_attribute(env.module, :__gettext_backend__)

raise ArgumentError, """
Gettext macros expect message keys (msgid and msgid_plural),
domains, and comments to expand to strings at compile-time, but the given #{what}
Expand Down
111 changes: 66 additions & 45 deletions test/gettext/macros_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -55,75 +55,87 @@ defmodule Gettext.MacrosTest do
end

test "pgettext/3, pngettext/4: dynamic context raises" do
code =
quote do
context = "test"
pgettext(context, "Hello world")
error =
assert_raise ArgumentError, fn ->
defmodule Sample do
use Gettext, backend: Gettext.MacrosTest.Translator
context = "test"
pgettext(context, "Hello world")
end
end

error = assert_raise ArgumentError, fn -> Code.eval_quoted(code, [], __ENV__) end
message = ArgumentError.message(error)
assert message =~ "Gettext macros expect message keys"
assert message =~ "{:context"
assert message =~ "Gettext.gettext(Gettext.MacrosTest.Translator, string)"

code =
quote do
context = "test"
pngettext(context, "Hello world", "Hello world", 5)
error =
assert_raise ArgumentError, fn ->
defmodule Sample do
use Gettext, backend: Gettext.MacrosTest.Translator
context = "test"
pngettext(context, "Hello world", "Hello world", 5)
end
end

error = assert_raise ArgumentError, fn -> Code.eval_quoted(code, [], __ENV__) end
message = ArgumentError.message(error)
assert message =~ "Gettext macros expect message keys"
assert message =~ "{:context"
assert message =~ "Gettext.gettext(Gettext.MacrosTest.Translator, string)"
end

test "dpgettext/4, dpngettext/5: dynamic context or dynamic domain raises" do
code =
quote do
context = "test"
dpgettext("default", context, "Hello world")
error =
assert_raise ArgumentError, fn ->
defmodule Sample do
use Gettext, backend: Gettext.MacrosTest.Translator
context = "test"
dpgettext("default", context, "Hello world")
end
end

error = assert_raise ArgumentError, fn -> Code.eval_quoted(code, [], __ENV__) end
message = ArgumentError.message(error)
assert message =~ "Gettext macros expect message keys"
assert message =~ "{:context"
assert message =~ "Gettext.gettext(Gettext.MacrosTest.Translator, string)"

code =
quote do
domain = "test"
dpgettext(domain, "test", "Hello world")
error =
assert_raise ArgumentError, fn ->
defmodule Sample do
use Gettext, backend: Gettext.MacrosTest.Translator
domain = "test"
dpgettext(domain, "test", "Hello world")
end
end

error = assert_raise ArgumentError, fn -> Code.eval_quoted(code, [], __ENV__) end
message = ArgumentError.message(error)
assert message =~ "Gettext macros expect message keys"
assert message =~ "{:domain"
assert message =~ "Gettext.gettext(Gettext.MacrosTest.Translator, string)"

code =
quote do
context = "test"
dpngettext("default", context, "Hello world", "Hello world", n)
error =
assert_raise ArgumentError, fn ->
defmodule Sample do
use Gettext, backend: Gettext.MacrosTest.Translator
context = "test"
dpngettext("default", context, "Hello world", "Hello world", n)
end
end

error = assert_raise ArgumentError, fn -> Code.eval_quoted(code, [], __ENV__) end
message = ArgumentError.message(error)
assert message =~ "Gettext macros expect message keys"
assert message =~ "{:context"
assert message =~ "Gettext.gettext(Gettext.MacrosTest.Translator, string)"

code =
quote do
domain = "test"
dpngettext(domain, "test", "Hello world", "Hello World", n)
error =
assert_raise ArgumentError, fn ->
defmodule Sample do
use Gettext, backend: Gettext.MacrosTest.Translator
context = "test"
dpngettext(domain, "test", "Hello world", "Hello World", n)
end
end

error = assert_raise ArgumentError, fn -> Code.eval_quoted(code, [], __ENV__) end
message = ArgumentError.message(error)
assert message =~ "Gettext macros expect message keys"
assert message =~ "{:domain"
Expand All @@ -136,37 +148,46 @@ defmodule Gettext.MacrosTest do
end

test "dgettext/3 and dngettext/2: non-binary things at compile-time" do
code =
quote do
msgid = "Invalid email address"
dgettext("errors", msgid)


error =
assert_raise ArgumentError, fn ->
defmodule Sample do
use Gettext, backend: Gettext.MacrosTest.Translator
context = "test"
dgettext("errors", msgid)
end
end

error = assert_raise ArgumentError, fn -> Code.eval_quoted(code, [], __ENV__) end
message = ArgumentError.message(error)
assert message =~ "Gettext macros expect message keys"
assert message =~ "{:msgid"
assert message =~ "Gettext.gettext(Gettext.MacrosTest.Translator, string)"

code =
quote do
msgid_plural = ~s(foo #{1 + 1} bar)
dngettext("default", "foo", msgid_plural, 1)
error =
assert_raise ArgumentError, fn ->
defmodule Sample do
use Gettext, backend: Gettext.MacrosTest.Translator
msgid_plural = ~s(foo #{1 + 1} bar)
dngettext("default", "foo", msgid_plural, 1)
end
end

error = assert_raise ArgumentError, fn -> Code.eval_quoted(code, [], __ENV__) end
message = ArgumentError.message(error)
assert message =~ "Gettext macros expect message keys"
assert message =~ "{:msgid_plural"
assert message =~ "Gettext.gettext(Gettext.MacrosTest.Translator, string)"

code =
quote do
domain = "dynamic_domain"
dgettext(domain, "hello")

error =
assert_raise ArgumentError, fn ->
defmodule Sample do
use Gettext, backend: Gettext.MacrosTest.Translator
domain = "dynamic_domain"
dgettext(domain, "hello")
end
end

error = assert_raise ArgumentError, fn -> Code.eval_quoted(code, [], __ENV__) end
message = ArgumentError.message(error)
assert message =~ "Gettext macros expect message keys"
assert message =~ "{:domain"
Expand Down

0 comments on commit 539b72c

Please sign in to comment.