tmjoen
Gettext domains with code generation
I’m trying to create some translations for my CMS resources, and I’m trying to automatically create Gettext domains for each resource.
defmodule MyApp.Projects.Project do
use B.Blueprint
import MyApp.Gettext
@singular "project"
@plural "projects"
# these macros basically build a map of translations
translations do
context :fields do
translate :title do
label t("Some label text")
placeholder t("Some placeholder")
end
end
end
end
The t macro lives in the used B.Blueprint and looks like this:
# ...
#
defmacro t(msgid) do
quote do
dgettext(@plural, msgid)
end
end
This doesn’t work of course:
** (ArgumentError) Gettext macros expect translation keys (msgid and msgid_plural),
domains, and comments to expand to strings at compile-time, but the given domain
doesn't. This is what the macro received:
{{:., [], [{:__aliases__, [counter: {MyApp.Projects.Project, 58}, alias: false], [:Module]}, :__get_attribute__]}, [], [{:__MODULE__, [counter: {MyApp.Projects.Project, 58}], Kernel}, :plural, 83]}
Is this doable in any way? I was hoping to not having to put all my translations in the “default.pot” file, and also not having to do t("projects", "msg here") 
Marked As Solved
tmjoen
Thanks for the __CALLER__.module hint!
I did some logging and it seems that the t macro is run before the module attribute is set, so I had to do a slight API adjustment:
defmodule MyApp.Projects.Project do
use B.Blueprint,
singular: "Project",
plural: "Projects"
import MyApp.Gettext
# these macros basically build a map of translations
translations do
context :fields do
translate :title do
label t("Some label text")
placeholder t("Some placeholder")
end
end
end
end
then in B.Blueprint
defmacro __using__(opts) do
Module.register_attribute(__CALLER__.module, :singular, accumulate: false)
Module.put_attribute(__CALLER__.module, :singular, Keyword.fetch!(opts, :singular))
Module.register_attribute(__CALLER__.module, :plural, accumulate: false)
Module.put_attribute(__CALLER__.module, :plural, Keyword.fetch!(opts, :plural))
# ...
Now the module attribute is available in my t macro! 
Also Liked
kip
Something like this should do the trick however for some reason Module.get_attribute/2 is returning nil which I did not expect. My early morning brain isn’t resolving why so putting here so smarter brains can clarify.
defmodule B.Blueprint do
defmacro t(msgid) do
caller = __CALLER__.module
# On my quick test this is returning `nil` which surprised me
domain = Module.get_attribute(caller, :plural)
# Testing this way works
domain = "plural"
quote do
dgettext(unquote(domain), unquote(msgid))
end
end
end
defmodule MyApp.Projects.Project do
import MyApp.Gettext
import B.Blueprint
@plural "projects"
t("some message")
end
kip
Cool, I think thats a better and more obvious API too. Glad its up and running for you!







