Eiji

Eiji

Extract gettext translations inside macro

Hi, I’m working on DSL and the last thing that I don’t like is how I’m extracting translations.

I have 2 version of code:

  1. My current implementation is based on Gettext.Extractor private API - it’s working without any issues

  2. An alternative is to use Gettext.Macros.dpgettext_noop_with_backend/4. It’s also a solution which works very well, but unfortunately it does not allow function generators (other macros, for loops etc.) as Gettext macros requires binary literals.

defmodule MyApp.Example do
  use MyLib.DSL, gettext_backend: MyApp.GettextBackend

  for data <- ~w[first second third] do
    some_dsl data
  end
end

I understand that private API is unstable and can be changed at any time, but I have simply no idea about other possible solutions. From now on I can see only 2 ways to fix it:

  1. Propose to make Gettext.Extractor a public API - that’s the simplest thing that could be done as it’s only about documenting code i.e. no change in code.

  2. Propose to support non-literals, so internal Gettext.Extractor calls are done within quote do … end block instead of directly inside macros.

Both solutions are rather something you consider at the very end. Support for gettext is only optional, so I don’t want to resign from function generators. The whole logic is done only in compile-time, so the generated code is as fast as possible in runtime.

With all above in mind I’m not satisfied in both implementations and I have no idea what to do with that. Did you had a similar problems with gettext when working on DSL? What do you think about making Gettext.Extractor public? Is there any other way to extract translations without above problems?

Marked As Solved

Eiji

Eiji

Generating a .pot file using expo is definitely the best solution. I have a full control over code, I don’t need to worry about private APIs and it’s also a very simple thing to do.

Also Liked

LostKobrakai

LostKobrakai

I’d love if Gettext.Extractor could become public api. I had asked for that a few months back on a more esoteric usecase and given the usecase the response was negative. This sounds more in line with the libraries goals. But I wonder why you cannot provide binary literals if you’re in a macro context.

maennchen

maennchen

This works with a slight change (needs a quote / unquote cycle, not sure why):

defmodule DummyGettext do
  defmacro dummy_macro(input, _lang \\ "en") do
    # gettext macros require literals at this point (not within quote do … end block)
    IO.inspect(input)
    is_binary(input) == false && raise "gettext fail here"
  end

  def get_locales, do: ["en"]
end

defmodule MyLib.DSL do
  defmacro __using__(_opts \\ []) do
    quote do
      import MyLib.DSL

      require DummyGettext

      def translated_input(input, locale \\ "en")
    end
  end

  defmacro some_dsl(input) do
    quote do
      def translated_input(unquote(input), locale) do
        DummyGettext.dummy_macro(unquote(input), locale)
      end
    end
  end
end

defmodule MyApp.Example do
  use MyLib.DSL

  # this one obviously works
  some_dsl "text to translate"

  # my macros are fine with that, but gettext requires literal at compile time
  # so below code fails
  for data <- ["first", "second", "third"] do
    some_dsl unquote(data)
  end
end

There’s no need to create a function implementation per message / locale since gettext will already do that for you.

maennchen

maennchen

True, it’s a bit ugly. I wonder if that variable could be expanded somehow since unquote clearly also seems to be able to.

Sure, that works as well. Just make sure to either use a different domain from normal gettext or to not add the elixir-autogen flag. Otherwise you will get conflicts.

Gettext is already defining a function body for each message / locale:

Doing it again will not make it faster :slight_smile:

Eiji

Eiji

No need to worry about that. I’m using a custom domain every time. :slight_smile:

Also I’m not using any flag, because I generate a .pot file every time the module is re-compiled, so any new change will regenerate the template (pot file). Then I let gettext handle .po files.

Good point, I did not know that … It looks very complicated, so let’s summarise it if anyone is interested:

  1. Function in Gettext module calls a generated backend function
  2. When compiling backend (use Gettext.Backend, …) @before_compile Gettext.Compiler is generated
  3. So before backend module compiles a compiler module is then generating a function from .po files

I understand that, but I still use it as I’m not returning just translated text, so I can simply translate a text at compile time or work with quoted gettext call when dealing with AST from DSL. :thinking:

Where Next?

Popular in Questions Top

ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New

Other popular topics Top

peerreynders
Manning 2016 Halloween weekend sale via Deal of the Day Friday, October 28 - Half off all MEAPs - code WM102816LT Saturday, October 29 ...
326 29600 154
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 49522 488
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New

We're in Beta

About us Mission Statement