Git Product home page Git Product logo

gettext's People

Contributors

amatalai avatar c4710n avatar char0n avatar goofansu avatar johantell avatar josevalim avatar juanperi avatar katafrakt avatar kianmeng avatar kipcole9 avatar lexmag avatar linusdm avatar maennchen avatar maufl avatar nathanl avatar olafura avatar paulhenrich avatar rakoth avatar roodion avatar roques avatar stevedomin avatar szymon-jez avatar teodiaz avatar tusooa avatar whatyouhide avatar wojtekmach avatar wpiekutowski avatar xadhoom avatar xtian avatar zurga avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gettext's Issues

Duplicate msgids

Opening this issue so that we don't forget this problem and we can discuss it here. Quoting what @josevalim wrote in #21:

we need to discuss what we are going to do with duplicated entries in the same .po file. What if we have the same msgid twice? It will probably emit a warning when compiling but the warnings won't be clear. We can probably keep a HashDict when we load the translations storing all msgid => line, if there is a duplicate, we could store it in a list which we return as result of parse, something like: {:dup_msgid, [line1, lin2, msgid]}. Then the entity calling parse can decide if it is going to raise, print errors, etc. This is the approach used by Erlang's compiler.

The proposed solution looks good to me, I'll give implementing it a try and we'll see how it works out :).

Alternative to missing interpolation keys error message

If you use interpolation to construct a string, and you provide it with missing keys, then it returns an error message telling you which keys are missing.

However, I need to use Gettext dynamically, and I would like it to do something a little different: If the key "key" is not present, then I would like it to return the string with place holder text "{key}".

For example, instead of this:

Localisation.Gettext.lgettext("en", "hello_world", "score-message", %{})
{:error, "missing interpolation keys: score"}

I would like it to return something like this:

Localisation.Gettext.lgettext("en", "hello_world", "score-message", %{})
{:ok, "You have scored {score} points."}

It would be great if the library could support this.

Unfortunately, it looks like this would be quite a big change, especially since the Interpolation module runs quite deeply in the code.

Is there any worth in creating a branch to implement this?

Extraction fails when using variable domain

As per https://groups.google.com/forum/#!topic/elixir-lang-talk/GyYMj_4oc4o, mix gettext.extract crashes when used with a variable domain name, such as dgettext(domain, "Say something")

** (Protocol.UndefinedError) protocol String.Chars not implemented for {:domain, [line: 7], nil}
    (elixir) lib/string/chars.ex:3: String.Chars.impl_for!/1
    (elixir) lib/string/chars.ex:17: String.Chars.to_string/1
    lib/gettext/extractor.ex:93: Gettext.Extractor.pot_path/2
    lib/gettext/extractor.ex:89: Gettext.Extractor.create_po_struct/3
    lib/gettext/extractor.ex:83: anonymous fn/3 in Gettext.Extractor.create_po_structs_from_extracted_translations/1
    (stdlib) lists.erl:1262: :lists.foldl/3
    lib/gettext/extractor.ex:81: Gettext.Extractor.create_po_structs_from_extracted_translations/1
    lib/gettext/extractor.ex:62: Gettext.Extractor.pot_files/0
    lib/mix/tasks/gettext.extract.ex:59: Mix.Tasks.Gettext.Extract.extract/0
    lib/mix/tasks/gettext.extract.ex:30: Mix.Tasks.Gettext.Extract.run/1
    (mix) lib/mix/cli.ex:58: Mix.CLI.run_task/2

Preferably I would not have it crash but rather just skip over the calls it cannot extract so that I can create those myself, and fill in the calls that it did extract.

Interpolation

Although gettext does not support interpolation, we could support it out of the box. The reasoning is that interpolation is common and supporting it out of the box is going to allow us to do some optimizations similar to the ones Chris did in linguist.

The idea is that lgettext and lngettext will receive an extra argument with interpolation: a map or a list (where a list is internally converted to a map). A naïve way of supporting interpolation is this:

def lgettext("pt", "default", "hello world %{something}", interpolation) do
  Gettext.Interpolation.interpolate("olá mundo %{something}", interpolation)
end

However, it would be better if we extracted all occurrence of "%{count}" or "%{whatever}" from strings during compilation time. In other words, we could compile to something like:

def lgettext("pt", "default", "hello world %{something}", interpolation) do
  case interpolation do
    # Check all keys are in interpolation
    %{something: something} -> {:ok, "olá mundo " <> something}
    # Otherwise detect which keys are missing and return an error
    _ -> {:error, Gettext.Interpolation.missing_interpolation_keys(interpolation, [:something])
  end
end

Where missing_interpolation_key will look at the interpolation map, the required keys, and return a string saying: "missing interpolation keys: foo, bar, something".

Note though we still need to have the runtime behaviour, because the default messages need to be translated at runtime, as we never compile them. So the fallback clause still is:

def lgettext(_locale, _context, default, interpolation) do
  case Gettext.Interpolation.interpolate(default, interpolation) do
    {:ok, interpolated} -> {:default, interpolated}
    {:error, _} = error -> error
  end
end

Thoughts?

Dialyzer warnings with OTP 19

Brand new phoenix app created with mix phoenix.new on OTP 19 has these warnings.

Here's a repro app: https://github.com/aaronjensen/dialyzer_repro

Irrelevant warnings are omitted. Originally reported at phoenixframework/phoenix#1872

Starting Dialyzer
dialyzer --no_check_plt --plt /Users/aaronjensen/.dialyxir_core_19_1.3.2.plt -Wunmatched_returns -Werror_handling -Wrace_conditions -Wunderspecs /Users/aaronjensen/Source/dialyzer_repro/_build/dev/lib/dialyzer_repro/ebin
  Proceeding with analysis...
gettext.ex:1: Function lngettext/6 has no local return
gettext.ex:1: The call 'Elixir.Gettext.Interpolation':interpolate(_@7::any(),_@8::#{'count':=_, _=>_}) breaks the contract (binary(),#{}) -> {'ok',binary()} | {'error',binary()}
gettext.ex:23: Function lngettext/5 has no local return
 done in 0m3.37s
done (warnings were emitted)

Extract dynamic translation calls

We wrote a hex package for phoenix that makes form handling easier.

Unfortunately, we didn't find any way to use the mix gettext.extract with it.

Example

%User{first_name: "....", last_name: "....."} (User model)

<%= input f, :first_name %> (User form)

It calles:

Gettext.dgettext(MyApp.Gettext, "forms", "user.first_name.label")

(source code)


Is there any way to automatically extract these translation-keys?

"mix test --stale" doesn't work with Gettext

I have to investigate why. It basically just runs as mix test without caching tests or even printing anything that mix test --stale usually prints. @antipax if you have any quick idea on why this could be, feel free to chime in :)

Wrong documentation?

I think the documentation for the plural forms might contain a mistake.
It says here that you can configure the pluralizer in config/config.exs but that did not work for me. Configuring it in the custom Gettext module when using use Gettext does work though.

Handwritten parser vs. yecc-generated parser

@josevalim and I have been discussing whether to use a handwritten parser or a yecc-generated parser for .po files. Initially, I was in favour of using yecc in order to have a very declarative and easy-to-understand grammar. After implementing a first version of the parser using yecc, I gave implementing a handwritten parser a try and it was practically just as easy.

Since I couldn't decide which way was the best one, I pushed both implementation to my fork so that we can decide together.

Please note that both parsers require (a lot) of polishing, in particular the yecc-based one since I'm not sure that stuffing the .yrl file in a src directory is the right way to go.

I'm looking forward to your opinions!

Syntax errors can be absolutely terrible

A PO file like this beauty (default.po):

#: lib/foo.ex:1
msgid "hello"

#: lib/foo.ex:2
msgid "world"
msgstr ""

Spits out a basically impossible-to-decipher error:

** (Gettext.PO.SyntaxError) 4: syntax error before: <<"#: lib/foo.ex:2">>

Why this sucks (as if it was need to show explicitly 😛):

  • no file name (wat!!!)
  • Erlang binary instead of Elixir binary
  • finally, it should just say something like "missing msgstr for msgid "hello""

@josevalim I'm afraid the only way to do this is to move the logic that builds translations from msgids and msgstrs outside of the .yrl parser, in Elixir-land? Wdyt?

Generating and merging PO(T) files

Opening this issue to lay down what @josevalim and I discussed yesterday. So, the next part in the development of Gettext for Elixir will be focused on compile-time niceties. These include automatic generation of POT and PO files as well as merging of such files.

First of all, just as a reference, POT files are just like PO files but with empty translations. Example of snippet of POT file:

msgid "Hello there!"
msgstr ""

msgid "One error"
msgid_plural "%{count} errors"
msgstr[0]
msgstr[1]

The first step for implementing all of this will be to create a Gettext.PO struct that will represent a PO file. We need this since PO files contain translations as well as metadata (headers at the top of the file).

After we have a Gettext.PO struct in place, we'll create the code that dumps an instance of this struct as a PO(T) file. The goal (and the ultimate test) will be to make this true:

dump(parse(file)) == file

Merging PO(T) files

Once this is done, the next step will be to write code that merges two PO(T) files. This is probably the trickiest thing to do. PO(T) files will be merged every time the user runs whatever task we'll provide and some PO(T) files already exist. During this step, we'll do this:

  • add all the new translations found in the source code
  • remove all the old translations that are not in the source code anymore; we'll only remove the translations with a #: reference comment (see #16 for some info on comments) assuming that we automatically generated them
  • leave translations without a reference comment untouched, even if they're not in the source code anymore

Generating PO files

This will be pretty easy at this point. We just need to tweak the *gettext macros we have now so that we know all the translations at compile time.

The future...

We can probably refine the process of merging PO(T) files, for example by not removing translations based on string similarities.


This is everything I remember from our conversation, @josevalim if I missed something add it here so we have everything in one place :).

Add mix gettext.extract --merge

The idea is that:

  1. The extractor layer will be able to track if a given .pot file effectively change or not
  2. If any .pot file changed for a given directory, for example "priv/gettext", we will call Mix.Tasks.Gettext.Merge.run ["priv/gettext"] once all .pot files for that directory are written

This way we can perform merge efficiently too.

Dialyzer output

mix.lock

"gettext": {:hex, :gettext, "0.11.0"},

Relevant mix.exs dialyzer configuration in project function

      dialyzer: [
        plt_add_deps: true,
        plt_file: ".local.plt"
      ]

On a brand new elixir 1.2.5 phoenix project, I get the following output from the dialyzer.

mix dialyzer

gettext.ex:1: The inferred type for the 1st argument of 'MACRO-dgettext'/3 ({_,_}) is not a supertype of #{}, which is expected type for this argument in the callback of the 'Elixir.Gettext.Backend' behaviour
gettext.ex:1: The inferred type for the 1st argument of 'MACRO-dgettext'/4 ({_,_}) is not a supertype of #{}, which is expected type for this argument in the callback of the 'Elixir.Gettext.Backend' behaviour
gettext.ex:1: The inferred type for the 1st argument of 'MACRO-dngettext'/5 ({_,_}) is not a supertype of #{}, which is expected type for this argument in the callback of the 'Elixir.Gettext.Backend' behaviour
gettext.ex:1: The inferred type for the 1st argument of 'MACRO-dngettext'/6 ({_,_}) is not a supertype of #{}, which is expected type for this argument in the callback of the 'Elixir.Gettext.Backend' behaviour

Add Gettext.known_locales(backend)

This function would return all the locales in a given backend for which there are PO files. This is useful to know in advance if the locale you want to translate to has translations file or if Gettext would fallback to english.

Remove ensure_yecc_compiler

This is just a reminder for us to eventually do it. When Phoenix 1.2 is released, it will be Elixir 1.2 only and Elixir 1.2 already includes its own warning, so it should be safe to remove the task from gettext. :)

Add comments to generated files

For .pot files we must say:

# msgids are often extracted from source code.
# Run "mix gettext.extract" to make them up to date.

For .po files we must say:

# msgids come from .pot files. Do not add, change or remove msgids manually, only msgstr.
# Use "mix gettext.extract --merge" or "mix gettext.merge" to merge .pot into .po.

We also need a mechanism to have those comments in pot files not leak into po.

Missing support for translation context

The original gettext library has pgettext, npgettext, dpgettext and dpngettext macros to provide different translations of the same phrases in different context. Those are very useful in case of there are short strings and their implementation is simple.

Terrible error message when passing a non binary to Gettext.*gettext functions

When a non binary value is passed to one of the Gettext.*gettext functions, the error is absolutely terrible (it goes down to Regex.split/3, making it super cryptic):

iex(3)> Gettext.dgettext MyApp.Gettext, "default", :foo
** (FunctionClauseError) no function clause matching in Regex.split/3
    (elixir) lib/regex.ex:375: Regex.split(~r/\n    (?<left>)  # Start, available through :left\n    %{         # Literal '%{'\n
    [^}]+    # One or more non-} characters\n    }          # Literal '}'\n    (?<right>) # End, available through :right\n  /x, :
foo, [on: [:left, :right], trim: true])
             lib/gettext/interpolation.ex:27: Gettext.Interpolation.to_interpolatable/1
             lib/gettext/interpolation.ex:99: Gettext.Interpolation.interpolate/2
    (my_app) lib/my_app/gettext.ex:1: MyApp.Gettext.lgettext/4
             lib/gettext.ex:480: Gettext.dgettext/4

gettext.extract --merge does not preserve default msgstr's

I may be missing something, but when running mix gettext.extract --merge for the first time, I find all the message strings are blank. This results in all templates returning blank strings until I go fill in the translations. It seems like a better approach would be to have the msgid copied over to the msgstr. Thoughts?

Example of generated .po

#: web/views/page_view.ex:3
msgid "A productive web framework that does not compromise speed and maintainability."
msgstr ""

#: web/views/layout_view.ex:19
msgid "Get Started"
msgstr ""

Example of what I'd like to be generated:

#: web/views/page_view.ex:3
msgid "A productive web framework that does not compromise speed and maintainability."
msgstr "A productive web framework that does not compromise speed and maintainability."

#: web/views/layout_view.ex:19
msgid "Get Started"
msgstr "Get Started"

Merging translations isn't an idempotent operation

If a translation has translator comments, then merging it with itself will concat the comments. Minimal case:

t = %Gettext.PO.Translation{comments: ["# a comment"]}

Gettext.PO.Translations.merge(t, t)
#=> %Gettext.PO.Translation{comments: ["# a comment", "# a comment"], ...}

Gettext.PO.Translations.merge(t, Gettext.PO.Translations.merge(t, t))
#=> %Gettext.PO.Translation{comments: ["# a comment", "# a comment", "# a comment"], ...}

Gettext.PO.Entry

As of today, the output we get from parsing a .po file is a list of translations (both plural and singular, [Gettext.PO.Translation | Gettext.PO.PluralTranslation]. This is fine since we only need to support a handful of features in .po files. More importantly, those features draw a clear line between singular and plural translations, since the only thing they share is a msgid (and even that has a slightly different meaning because it needs a msgid_plural counterpart in a plural translation).

Eventually, the number of features we need to support will grow since gettext supports other stuff (like contexts) and we also have to understand comments in .po files and so on.

Since the differences between singular and plural translations are pretty much only the msgid(_plural)? and msgstr parts, we would have to duplicate all the other data about them (comments, context) in both structs. I think we want to avoid that, so I'm proposing to introduce a Gettext.PO.Entry struct, the @type of which would look something like:

@type %Gettext.PO.Entry{
  translation: Gettext.PO.Translation.t | Gettext.PO.PluralTranslation.t,
  context: binary,
  comments: %{
    translator: [binary],
    ref: ...,
    ...
  } 
}

The name is inspired by the ruby-gettext project, which has a POEntry class.

The format I suggested is just a proof of concept, I'm sure we could improve it. What do you think?

Msgids are deleted from POT files

I read through the issues and found that it's a feature, but unfortunately not for me.
When running mix gettext.extract all msgids from POT files that have a reference are deleted if they are not present in the extracted msgids.

I'm trying to extract gettext msgids from React components that we use in our Phoenix project.
Phoenix uses Webpack to bundle frontend assets and I found this handy loader that will extract msgids from Js(x) files when they are loaded via webpack.
All the msgids are dumped to a new POT file in priv/gettext (because I configured it so) and they all have references.
Now when I extract msgid using mix, the POT file is emptied :(

I can't change the behaviour of the loader without patching jsxgettext and jsxgettext-loader.
I could put the POT file in another directory, but right now the workflow for Phoenix and Reactis perfectly in sync and I would like to keep it that way.

Is there a possibility to force (Elixir) gettext to not delete msgids from a POT file?

Updated comments changes ignored

Issue:
Updated comments in the .pot file don't seem to have the change propogated to the .po files on running mix gettext.merge priv/gettext

In .pot files:

# This is a pot file and this is a comment
msgid "foo"
msgstr ""

changed to

# Sorry wrong comment first time
msgid "foo"
msgstr ""

doesn't seem to update in the .po files

Task gettext.extract does not work when used last in an alias

I just stumbled upon a problem with the mix gettext.extract task. In our mix.exs file we define an alias like this:

      "gettext.extract": ["custom.gettext.extract", "gettext.extract"]

This does not work :( The code gets compiled but the POT file is never written.
However:

      "gettext.extract": ["gettext.extract", "custom.gettext.extract"]

does work.

Our custom extract task looks like this.

  def run(_) do
    node_env = case Application.get_env(:papyrus_psp, :environment) do
      :dev   -> "development"
      :prod  -> "production"
      :test  -> "test"
      _      -> "unknown"
    end

    case Mix.Shell.IO.cmd(command(node_env)) do
      0 -> :ok
      _ -> :error
    end
  end

  defp command(node_env) do
    "NODE_ENV=#{node_env} $(npm bin)/react-gettext-parser --output priv/gettext/react.pot 'web/static/js/**/*.{js,jsx}' '!web/static/js/i18n.js'"
  end

It's not really a problem for us, but maybe someone else will run into the same problem, so I wanted to document it here.

(Gettext.Error) missing interpolation keys

Got:

 ** (Gettext.Error) missing interpolation keys: a
    (gettext) lib/gettext.ex:653: Gettext.handle_backend_result/1

when call:
gettext("test %{a} %{a}", a: 1)

For
gettext("test %{a}", a: 1)
everything is fine

gettext fails to compile in Phoenix 1.1.0 installation

Hello, when trying to compile dependencies in a new Phoenix 1.1.0 application the libary gettext fails to compile. It seems to not recgonize a module called :yecc. I have reproduced the error with some other info that may be useful. let me know if there anything else you need.

$ elixir --version
Elixir 1.1.0
$ erl -eval 'erlang:display(erlang:system_info(otp_release)), halt().' -noshell
"18"
$ uname -a
Linux worker 3.13.0-57-generic #95-Ubuntu SMP Fri Jun 19 09:28:15 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
$ mix archive.install https://github.com/phoenixframework/phoenix/releases/download/v1.1.0/phoenix_new-1.1.0.ez
Found existing archive(s): phoenix_new-1.0.4.ez.
Are you sure you want to replace them? [Yn] Y
creating .mix/archives/phoenix_new-1.1.0.ez
$ mix phoenix.new example
...
$ cd example
$ cat mix.lock
%{"connection": {:hex, :connection, "1.0.2"},
"cowboy": {:hex, :cowboy, "1.0.4"},
"cowlib": {:hex, :cowlib, "1.0.2"},
"decimal": {:hex, :decimal, "1.1.0"},
"ecto": {:hex, :ecto, "1.1.0"},
"fs": {:hex, :fs, "0.9.2"},
"gettext": {:hex, :gettext, "0.9.0"},
"phoenix": {:hex, :phoenix, "1.1.0"},
"phoenix_ecto": {:hex, :phoenix_ecto, "2.0.0"},
"phoenix_html": {:hex, :phoenix_html, "2.3.0"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.0.1"},
"plug": {:hex, :plug, "1.0.3"},
"poison": {:hex, :poison, "1.5.0"},
"poolboy": {:hex, :poolboy, "1.5.1"},
"postgrex": {:hex, :postgrex, "0.10.0"},
"ranch": {:hex, :ranch, "1.2.0"}}
$ mix deps.compile
==> connection
Compiled lib/connection.ex
Generated connection app
==> poolboy (compile)
Compiled src/poolboy_worker.erl
Compiled src/poolboy_sup.erl
Compiled src/poolboy.erl
==> decimal
Compiled lib/decimal.ex
Generated decimal app
==> poison
Compiled lib/poison.ex
Compiled lib/poison/decoder.ex
Compiled lib/poison/parser.ex
Compiled lib/poison/encoder.ex
Generated poison app
==> gettext
could not compile dependency :gettext, "mix compile" failed. You can recompile this dependency with "mix deps.compile gettext", update it with "mix deps.update gettext" or clean it with "mix deps.clean gettext"
** (UndefinedFunctionError) undefined function: :yecc.file/2 (module :yecc is not available)
:yecc.file('src/gettext_po_parser.yrl', [parserfile: 'src/gettext_po_parser.erl', report: true])
(mix) lib/mix/compilers/erlang.ex:84: anonymous fn/3 in Mix.Compilers.Erlang.compile/3
(elixir) lib/enum.ex:1385: Enum."-reduce/3-lists^foldl/2-0-"/3
(mix) lib/mix/compilers/erlang.ex:83: Mix.Compilers.Erlang.compile/3
(elixir) lib/enum.ex:1043: anonymous fn/3 in Enum.map/2
(elixir) lib/enum.ex:1385: Enum."-reduce/3-lists^foldl/2-0-"/3
(elixir) lib/enum.ex:1043: Enum.map/2
(mix) lib/mix/tasks/compile.all.ex:19: anonymous fn/1 in Mix.Tasks.Compile.All.run/1

Gettext public API

For now all the work has been done on Gettext internals and it is time to discuss the public API. What I would like to call is this:

MyApp.Gettext.gettext "hello world"

And this does a couple things:

  1. It is a macro because we want to collect those strings to automatically generate .po files. In order to be collected, the first argument needs to be a string at compilation time.
  2. The locale is implicit. Which means it needs to be stored in the process dictionary per gettext module. The key in the process dictionary can be the module itself, the default is :en but the default can be customized with the :default_locale option on use. What is the official gettext API for setting locale?

However, this discussion brings one main question: what should be the API for doing dynamic lookups?

One option is to do the dynamic lookup still using the gettext macro:

    string = "this still works"
    MyApp.Gettext.gettext string

And then, when generating the default .po file, we would warn on such usages so users remember of checking those strings into their .po files.

Another option though is to ask the users to go through the currently "private" lgettext function for doing the dynamic lookup. In doing such, they would need to explicitly pass the locale, domain and what not.

The third and final option is to introduce another set of functions that would be used for the dynamic lookup. Or even provide an API in the main Gettext module itself:

  string = "this still works"
  Gettext.gettext(MyApp.Gettext, string)

The rationale behind this last one is that for dynamic lookups the gettext module will likely be dynamic too, so provide an API that receives both is ok.

Thoughts?

Comments tokenizing + parsing

Comments in Gettext are not like comments everywhere else, since you usually don't just ignore them.

Comments start with an hash (#) character and go on until EOL. There are two main kinds of comments:

  • translator comments (denoted by # followed by some whitespace, most commonly #[space])
  • autogenerated comments (# followed by some specifier character)

We will need to parse, tokenize and keep all those comments associated with each translation since they are necessary to rebuild PO files exactly like we found them. Also, we can obviously make use of the autogenerated comments.

Translator comments

# This is a translator comment.
msgid "foo"
msgstr "bar"

We'll keep them as is, nice and easy.

Autogenerate comments

#. extracted-comments
#: reference
#, flag
#| msgid previous-untranslated-string

Extracted comments: they're written by the programmer, in the source code, for the translator. I think we should avoid implementing this for now as we would need some kind of macro to do this as well.

#. Hey translator, thanks!
...

Reference: file or list of files the next translation comes from. This is the easiest kind of comment to implement. If two translations with the same msgid are found, they're merged into one translation and both source files are listed here.

#: my_app/lib/foo.ex:84 my_app/lib/bar.ex:40
...

Flag: a list of flags/tags/format directives. Mostly used to denote the source language. We'll most likely automatically add elixir-format here.

#, elixir-format
...

Fuzzy: related to the fuzzy flag. Let's just leave this alone for now :|

We're going to add a :comments field to the PluralTranslation and Translation structs, I'll think about the data structure for this. I'd go for a simple map or a Comments struct, but we have to decide if the original order of comments found in PO files matters to us (when re-creating the PO files back). If it does, we can go with a list of tagged tuples like [{:reference, ...}, {:flags, ...}]. We'll see :)

Add support for extracting developers comments

PO files provide very helpful feature called developer comments. It will be good if gettext.extract with support it.

gettext("Navigation") # Will be on top of page.
#: web/views/pages/sidebar_view.ex:100
#. "Will be on top of page."
msgid "Navigation"
msgstr ""

Consider having default locale set is a must

otherwise we need a more informative error message for a following case:

iex(1)> Gettext.locale
nil
iex(2)> Gettext.with_locale("it", fn() -> end)
** (ArgumentError) locale/1 only accepts binary locales
    lib/gettext.ex:432: Gettext.locale/1
    lib/gettext.ex:568: Gettext.with_locale/2
iex(3)> Gettext.locale
"it"

As you see without :default_locale env var it fails to reset locale back.

Default pluralizer does not have fallback for unknown locales

Hi, it's me again ^^
The documentation says:

Locale

At runtime, all gettext-related functions and macros that do not explicitely take a locale as an argument read the locale from Gettext.get_locale/1. The locale can be set with Gettext.put_locale/2. Locales are expressed as strings (like "en" or "fr"); they can be arbitrary strings as long as they match a directory name.

However, I just found that this locale will be passed to Gettext.Plural.plural if no other pluralizer is configured. I get a no function clause matching for the locale nb-no.

I think there should be a catch all clause to avoid problems when people use strange locales.

gettext fails to compile with phoenix 1.1

This is similar to, but slightly different than:

#67
phoenixframework/phoenix#1411

mix phoenix.server

== Compilation error on file lib/gettext/backend.ex ==
** (CompileError) lib/gettext/backend.ex:16: undefined function ::/2
    (elixir) expanding macro: Kernel.@/1
    lib/gettext/backend.ex:16: Gettext.Backend (module)
    (elixir) lib/kernel/parallel_compiler.ex:100: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/8

==> gettext
could not compile dependency gettext, mix compile failed. You can recompile this dependency with `mix deps.compile gettext` or update it with `mix deps.update gettext`

I got erlang-parsetools and erlang-dev installed, and the error is same on ubuntu 15.10 and 16.04. Help please?

Collecting translations from the *gettext family of macros

I think this is almost the last step before a first usable version of Gettext, so I'm excited to discuss this topic :D

So, we need to generate POT files from calls to the *gettext family of macros. We already have these macros working, so the work we have to do in the macros themselves is probably just a matter of adding a couple lines to each macro:

defmacro gettext(...) do
  # ...
  if we_want_to_collect_translations do
    Gettext.Collector.collect(translation)
  end
  # ...
end

@josevalim, @ericmj, and I discussed possible strategies for "triggering" this POT file generation at ElixirConfEU 2015. These are the alternatives I remember from the conversation:

  • always generate POT files, whenever compilation happens. I'm not a fan of this strategy since, as José mentioned back then, it's probably better to be explicit and wait for the user to ask Gettext to collect translations.
  • provide a new mix task that collects translations. This is the approach I like the most; we can start an agent or create an ETS table (or whatever storage we'll choose) when the task is executed, then force recompiling of the project (all translations are stored in the agent/ETS table) and finally generate the POT file and merge it with the existing PO(T) files. FWIW, I think if we choose the mix task route it would possibly be better to provide a separate task for merging the new POT file with the old PO files so that users are free to merge them with other tools if they want to; for example, mix gettext.pot and mix gettext.merge_po or something on these lines.

I can't remember anything else from the conversation :( What are your ideas on this?

Cannot read multiple #: references on one line

When I use Poedit it saves the .po file like this:

#: web/views/dashboard_view.ex:5 web/views/dashboard_view.ex:10
#: web/views/dashboard_view.ex:20
msgid "Memory"
msgstr "Minne"

with multiple source code references on same line (there is a wrap-at preference setting in poedit).

This makes gettext choke:

== Compilation error on file lib/brando/gettext.ex ==
** (MatchError) no match of right hand side value: ["web/views/dashboard_view.ex", "5 web/views/dashboard_view.ex", "10"]
    lib/gettext/po/parser.ex:53: Gettext.PO.Parser.parse_reference/1
    lib/gettext/po/parser.ex:47: anonymous fn/2 in Gettext.PO.Parser.extract_references/1
    (elixir) lib/enum.ex:1385: Enum."-reduce/3-lists^foldl/2-0-"/3
    lib/gettext/po/parser.ex:45: Gettext.PO.Parser.extract_references/1
    lib/gettext/po/parser.ex:34: Gettext.PO.Parser.to_struct/1
    (elixir) lib/enum.ex:1043: anonymous fn/3 in Enum.map/2
    (elixir) lib/enum.ex:1385: Enum."-reduce/3-lists^foldl/2-0-"/3
    (elixir) lib/enum.ex:1043: Enum.map/2

This looks to be a valid way to print references, at least according to the GNU gettext manual:

#: src/msgcmp.c:338 src/po-lex.c:699
#, c-format
msgid "found %d fatal error"
msgid_plural "found %d fatal errors"
msgstr[0] "s'ha trobat %d error fatal"
msgstr[1] "s'han trobat %d errors fatals"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.