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:

https://github.com/elixir-gettext/gettext/blob/3a6e81ce64ae580af300c2abf825fef310314b2c/lib/gettext/compiler.ex#L605-L615

Doing it again will not make it faster :slight_smile:

Where Next?

Popular in Questions Top

sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
JulienCorb
I am trying to implement my new.html.eex file to create new posts on my website. new.html.eex: &lt;h1&gt;Create Post&lt;/h1&gt; &lt;%= ...
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
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
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Other popular topics Top

danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 29377 241
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 41539 114
New
greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
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 52341 488
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
AstonJ
Please see the new poll here: Which code editor or IDE do you use? (Poll) (2022 Edition) It’s been a while since we first asked this, I...
208 31142 143
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New

We're in Beta

About us Mission Statement