Skip to content

Commit

Permalink
Add example gettext
Browse files Browse the repository at this point in the history
  • Loading branch information
Theuns-Botha authored and fhunleth committed Jun 21, 2019
1 parent 9737ac6 commit a56cebe
Show file tree
Hide file tree
Showing 11 changed files with 312 additions and 3 deletions.
1 change: 0 additions & 1 deletion apps/nerves_hub_www/.gitignore
Expand Up @@ -22,4 +22,3 @@ erl_crash.dump

node_modules/
assets/yarn-error.log
/priv/
95 changes: 95 additions & 0 deletions apps/nerves_hub_www/README.md
@@ -1 +1,96 @@
# NervesHubWWW

Please note: These commands should be executed in the NervesHubWWW root dir
and not the nerves_hub_web root dir.

## Adding translations

### Replace the current text with a call to the gettext macro

Note: The extract and merge functionality only works if the macro is used.
So please do not use a function call like `NervesHubWWWWeb.Gettext.gettext "Name"` for translations.

Example:
```
<%= label f, :username, "Name" %>
```

Becomes:
```
<%= label f, :username, gettext "Name" %>
```

### Extract the translations to a .pot file

This creates a `.pot` file which is essentially a summary of all the places in
your code where you have used one of the gettext macros `gettext`, `ngettext`, `dgettext` or `dngettext`.

Run: `mix gettext.extract`


### Merge the translations into .po files

This creates/updates the `.po` files which are the files where your actual translations live.

It will create files if they do not already exist, and it will update files that do already exist.

Run `mix gettext.merge priv/gettext` to merge the `.pot` contents for all the supported locales.

Run `mix gettext.merge priv/gettext --locale {locale_name}` to merge the `.pot` contents for a new locale or a single existing locale.

Note that if you want some sort of "default translation", add them in the `.pot` file,
this will propogate that text to the individual `.po` files..

### Add the translated text

Add your translations by editing the values for `msgstr` in the `.po` files.

If no translation is given, the value contained in `msgid` will be used.

## Advanced translations

### Interpolated strings

This works a lot like the normal gettext functionality, the difference being that
you pass a value into the macro by binding it to a variable name (very similar to assigns in phoenix render functions).

You add such a translation by adding to the UI something like this:

```
<%= gettext("We would like to welcome you to the %{organization_name} family", organization_name: "Nerves Developers") %>
```

Now, run `gettext.extract` and `gettext.merge` as discussed before. This will add
the string to the `.pot` and `.po` files.

You can then add translations for the string by editing the `msgstr` in your `.po` files.

Translation definition:

```
msgid "We would like to welcome you to the %{organization_name} family"
msgstr "Ons wil you graag welkom heed by die %{organization_name} familie"
```

### Domain specific translations

By default, when using the `gettext` macro in our code, strings/tranlations gets
defined in `default.pot` and a `default.po` file for each locale.

When these files become too large, we can change over to domain based definition
by using the `dgettext` macro(and `dngettext` for plurals).

This causes certain translations to be moved to domain specific `.pot` and `.po`
files. Please see the gettext docs for more details, or take a look in nerves_hub_web
at the implimentation for "error" domain.

### Plural translations

The process for adding a plural to the translations is similar to what was discussed before.

Note that in the UI you use the macros that contain an `n` character such as `ngettext` and `dngettext`.

The `n` indicates that we are interested in a pluralized string.

Please see the gettext docs for more details.
2 changes: 2 additions & 0 deletions apps/nerves_hub_www/config/config.exs
Expand Up @@ -11,6 +11,8 @@ config :nerves_hub_www,
# Options are :ssl or :header
websocket_auth_methods: [:ssl]

config :nerves_hub_www, NervesHubWWWWeb.Gettext, default_locale: "en"

# Configures the endpoint
config :nerves_hub_www, NervesHubWWWWeb.Endpoint,
url: [host: System.get_env("HOST")],
Expand Down
60 changes: 60 additions & 0 deletions apps/nerves_hub_www/lib/nerves_hub_www_web/plugs/set_locale.ex
@@ -0,0 +1,60 @@
defmodule NervesHubWWWWeb.Plugs.SetLocale do
import Plug.Conn

def init(default), do: default

def call(conn, _config) do
conn
|> get_locale_from_header()
|> case do
nil ->
conn
|> put_resp_header(
"content-language",
Application.get_env(:nerves_hub_www, NervesHubWWWWeb.Gettext)[:default_locale]
)

locale ->
Gettext.put_locale(NervesHubWWWWeb.Gettext, locale)

conn
|> put_resp_header("content-language", locale)
end
end

defp get_locale_from_header(conn) do
conn
|> extract_accept_language()
|> Enum.find(nil, fn accepted_locale ->
Gettext.known_locales(NervesHubWWWWeb.Gettext)
|> Enum.member?(accepted_locale)
end)
end

defp extract_accept_language(conn) do
case Plug.Conn.get_req_header(conn, "accept-language") do
[value | _] ->
value
|> String.split(",")
|> Enum.map(&parse_language_option/1)
|> Enum.sort(&(&1.quality > &2.quality))
|> Enum.map(& &1.tag)
|> Enum.reject(&is_nil/1)

_ ->
[]
end
end

defp parse_language_option(string) do
captures = Regex.named_captures(~r/^\s?(?<tag>[\w\-]+)(?:;q=(?<quality>[\d\.]+))?$/i, string)

quality =
case Float.parse(captures["quality"] || "1.0") do
{val, _} -> val
_ -> 1.0
end

%{tag: captures["tag"], quality: quality}
end
end
1 change: 1 addition & 0 deletions apps/nerves_hub_www/lib/nerves_hub_www_web/router.ex
Expand Up @@ -8,6 +8,7 @@ defmodule NervesHubWWWWeb.Router do
plug(Phoenix.LiveView.Flash)
plug(:protect_from_forgery)
plug(:put_secure_browser_headers)
plug(NervesHubWWWWeb.Plugs.SetLocale)
end

pipeline :logged_in do
Expand Down
@@ -1,5 +1,5 @@
<p>
You will be added to the <%= @org.name %> org
<%= gettext "You will be added to the %{organization_name} org", organization_name: @org.name %>
</p>

<%= form_for @changeset, account_path(@conn, :accept_invite, @token), [as: :user, method: "post"], fn f -> %>
Expand Down
Expand Up @@ -5,7 +5,7 @@
</div>
<div class="modal-body">
<div class="form-group">
<label for="username">Username</label>
<label for="username"><%= gettext "Username" %></label>
<%= text_input f, :username, class: "form-control", id: "username" %>
<div class="has-error"><%= error_tag f, :username %></div>
</div>
Expand Down
22 changes: 22 additions & 0 deletions apps/nerves_hub_www/priv/gettext/af/LC_MESSAGES/default.po
@@ -0,0 +1,22 @@
## `msgid`s in this file come from POT (.pot) files.
##
## Do not add, change, or remove `msgid`s manually here as
## they're tied to the ones in the corresponding POT file
## (with the same domain).
##
## Use `mix gettext.extract --merge` or `mix gettext.merge`
## to merge POT files into PO files.
msgid ""
msgstr ""
"Language: af\n"
"Plural-Forms: nplurals=2\n"

#, elixir-format
#: lib/nerves_hub_www_web/templates/account/new.html.eex:8
msgid "Username"
msgstr "Gebruikersnaam"

#, elixir-format
#: lib/nerves_hub_www_web/templates/account/invite.html.eex:2
msgid "You will be added to the %{organization_name} org"
msgstr "Jy sal by die %{organization_name} org bygevoeg word"
87 changes: 87 additions & 0 deletions apps/nerves_hub_www/priv/gettext/af/LC_MESSAGES/errors.po
@@ -0,0 +1,87 @@
## `msgid`s in this file come from POT (.pot) files.
##
## Do not add, change, or remove `msgid`s manually here as
## they're tied to the ones in the corresponding POT file
## (with the same domain).
##
## Use `mix gettext.extract --merge` or `mix gettext.merge`
## to merge POT files into PO files.
msgid ""
msgstr ""
"Language: af\n"
"Plural-Forms: nplurals=2\n"

msgid "can't be blank"
msgstr ""

msgid "has already been taken"
msgstr ""

msgid "is invalid"
msgstr ""

msgid "must be accepted"
msgstr ""

msgid "has invalid format"
msgstr ""

msgid "has an invalid entry"
msgstr ""

msgid "is reserved"
msgstr ""

msgid "does not match confirmation"
msgstr ""

msgid "is still associated with this entry"
msgstr ""

msgid "are still associated with this entry"
msgstr ""

msgid "should be %{count} character(s)"
msgid_plural "should be %{count} character(s)"
msgstr[0] ""
msgstr[1] ""

msgid "should have %{count} item(s)"
msgid_plural "should have %{count} item(s)"
msgstr[0] ""
msgstr[1] ""

msgid "should be at least %{count} character(s)"
msgid_plural "should be at least %{count} character(s)"
msgstr[0] ""
msgstr[1] ""

msgid "should have at least %{count} item(s)"
msgid_plural "should have at least %{count} item(s)"
msgstr[0] ""
msgstr[1] ""

msgid "should be at most %{count} character(s)"
msgid_plural "should be at most %{count} character(s)"
msgstr[0] ""
msgstr[1] ""

msgid "should have at most %{count} item(s)"
msgid_plural "should have at most %{count} item(s)"
msgstr[0] ""
msgstr[1] ""

msgid "must be less than %{number}"
msgstr ""

msgid "must be greater than %{number}"
msgstr ""

msgid "must be less than or equal to %{number}"
msgstr ""

msgid "must be greater than or equal to %{number}"
msgstr ""

msgid "must be equal to %{number}"
msgstr ""
21 changes: 21 additions & 0 deletions apps/nerves_hub_www/priv/gettext/default.pot
@@ -0,0 +1,21 @@
## This file is a PO Template file.
##
## `msgid`s here are often extracted from source code.
## Add new translations manually only if they're dynamic
## translations that can't be statically extracted.
##
## Run `mix gettext.extract` to bring this file up to
## date. Leave `msgstr`s empty as changing them here as no
## effect: edit them in PO (`.po`) files instead.
msgid ""
msgstr ""

#, elixir-format
#: lib/nerves_hub_www_web/templates/account/new.html.eex:8
msgid "Username"
msgstr ""

#, elixir-format
#: lib/nerves_hub_www_web/templates/account/invite.html.eex:2
msgid "You will be added to the %{organization_name} org"
msgstr ""
22 changes: 22 additions & 0 deletions apps/nerves_hub_www/priv/gettext/en/LC_MESSAGES/default.po
@@ -0,0 +1,22 @@
## `msgid`s in this file come from POT (.pot) files.
##
## Do not add, change, or remove `msgid`s manually here as
## they're tied to the ones in the corresponding POT file
## (with the same domain).
##
## Use `mix gettext.extract --merge` or `mix gettext.merge`
## to merge POT files into PO files.
msgid ""
msgstr ""
"Language: en\n"
"Plural-Forms: nplurals=2\n"

#, elixir-format
#: lib/nerves_hub_www_web/templates/account/new.html.eex:8
msgid "Username"
msgstr ""

#, elixir-format
#: lib/nerves_hub_www_web/templates/account/invite.html.eex:2
msgid "You will be added to the %{organization_name} org"
msgstr ""

0 comments on commit a56cebe

Please sign in to comment.