Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F118484857
D168.1775833313.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
18 KB
Referenced Files
None
Subscribers
None
D168.1775833313.diff
View Options
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,5 +1,18 @@
## Development
-$ cd larus
-$ mix deps.get
-$ mix phoenix.server
+ $ cd larus
+ $ mix deps.get
+ $ npm install
+ $ mix phoenix.server
+
+## l10n
+
+Add supported locales to `larus/config/config.exs`.
+
+Extract strings:
+
+ $ mix gettext.extract
+
+Merge in new translated strings:
+
+ $ mix gettext.merge priv/gettext/ --locale de
diff --git a/larus/config/config.exs b/larus/config/config.exs
--- a/larus/config/config.exs
+++ b/larus/config/config.exs
@@ -14,6 +14,9 @@
pubsub: [name: Larus.PubSub,
adapter: Phoenix.PubSub.PG2]
+config :larus, Larus.Gettext,
+ locales: ~w(en de nl)
+
# Configures Elixir's Logger
config :logger, :console,
format: "$time $metadata[$level] $message\n",
diff --git a/larus/lib/blank.ex b/larus/lib/blank.ex
new file mode 100644
--- /dev/null
+++ b/larus/lib/blank.ex
@@ -0,0 +1,67 @@
+defmodule Blank do
+ @moduledoc """
+ Tools around checking and handling undefined or blank data.
+ """
+
+ @doc """
+ Returns `true` if data is considered blank/empty.
+ """
+ def blank?(data) do
+ Blank.Protocol.blank?(data)
+ end
+
+ @doc """
+ Returns `true` if data is not considered blank/empty.
+ """
+ def present?(data) do
+ !blank?(data)
+ end
+
+ @doc """
+ Returns the provided `data` if valid of the `default` value if not.
+ """
+ def default_to(data, default) do
+ if blank?(data), do: default, else: data
+ end
+end
+
+defprotocol Blank.Protocol do
+ @moduledoc """
+ Provides only one single method definition `blank?/1`
+ """
+
+ @doc """
+ Returns `true` if data is considered blank/empty.
+ """
+ def blank?(data)
+end
+
+# Integers are never blank
+defimpl Blank.Protocol, for: Integer do
+ def blank?(_), do: false
+end
+
+defimpl Blank.Protocol, for: BitString do
+ def blank?(""), do: true
+ def blank?(_), do: false
+end
+
+# Just empty list is blank
+defimpl Blank.Protocol, for: List do
+ def blank?([]), do: true
+ def blank?(_), do: false
+end
+
+defimpl Blank.Protocol, for: Map do
+ # Keep in mind we could not pattern match on %{} because
+ # it matches on all maps. We can however check if the size
+ # is zero (and size is a fast operation).
+ def blank?(map), do: map_size(map) == 0
+end
+
+# Just the atoms false and nil are blank
+defimpl Blank.Protocol, for: Atom do
+ def blank?(false), do: true
+ def blank?(nil), do: true
+ def blank?(_), do: false
+end
\ No newline at end of file
diff --git a/larus/lib/larus/plug/locale.ex b/larus/lib/larus/plug/locale.ex
new file mode 100644
--- /dev/null
+++ b/larus/lib/larus/plug/locale.ex
@@ -0,0 +1,64 @@
+defmodule Larus.Plug.Locale do
+ require Logger
+
+ import Plug.Conn
+
+ def init(default) do
+ default
+ end
+
+ def call(conn, default) do
+ locale = conn.params["locale"]
+
+ if locale in Larus.Gettext.supported_locales do
+ conn |> assign_locale! locale
+ else
+ locale = List.first(extract_locale(conn)) || default
+
+ conn |> assign_locale! locale
+ end
+ end
+
+ defp assign_locale!(conn, value) do
+ Logger.debug "Assigning locale #{inspect value}"
+ Gettext.put_locale(Larus.Gettext, value)
+ conn
+ |> assign(:locale, value)
+ end
+
+ defp extract_locale(conn) do
+ if Blank.present? conn.params["locale"] do
+ [conn.params["locale"] | extract_accept_language(conn)]
+ else
+ extract_accept_language(conn)
+ end
+ # Filter for only known locales
+ |> Enum.filter(fn locale -> Enum.member?(Larus.Gettext.supported_locales, locale) end)
+ end
+
+ defp extract_accept_language(conn) do
+ case conn |> get_req_header("accept-language") do
+ [value|_] ->
+ value
+ |> String.split(",")
+ |> Enum.map(&parse_language_option/1)
+ |> Enum.sort(&(&1.quality > &2.quality))
+ |> Enum.map(&(&1.tag))
+ _ ->
+ []
+ end
+ end
+
+ defp parse_language_option(string) do
+ captures = ~r/^(?<tag>[\w\-]+)(?:;q=(?<quality>[\d\.]+))?$/i
+ |> Regex.named_captures(string)
+
+ quality = case Float.parse(captures["quality"] || "1.0") do
+ {val, _} -> val
+ _ -> 1.0
+ end
+
+ %{tag: captures["tag"], quality: quality}
+ end
+
+end
diff --git a/larus/priv/gettext/de/LC_MESSAGES/default.po b/larus/priv/gettext/de/LC_MESSAGES/default.po
new file mode 100644
--- /dev/null
+++ b/larus/priv/gettext/de/LC_MESSAGES/default.po
@@ -0,0 +1,23 @@
+## `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: de\n"
+
+#: web/templates/page/index.html.eex:2
+msgid "Welcome to %{name}"
+msgstr ""
+
+#: web/views/layout_view.ex:9
+msgid "Larus flying high."
+msgstr ""
+
+#: web/views/layout_view.ex:13
+msgid "[YOUR TITLE HERE]"
+msgstr ""
diff --git a/larus/priv/gettext/errors.pot b/larus/priv/gettext/de/LC_MESSAGES/errors.po
copy from larus/priv/gettext/errors.pot
copy to larus/priv/gettext/de/LC_MESSAGES/errors.po
--- a/larus/priv/gettext/errors.pot
+++ b/larus/priv/gettext/de/LC_MESSAGES/errors.po
@@ -1,45 +1,42 @@
-## 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`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: de\n"
-## From Ecto.Changeset.cast/4
msgid "can't be blank"
msgstr ""
-## From Ecto.Changeset.unique_constraint/3
msgid "has already been taken"
msgstr ""
-## From Ecto.Changeset.put_change/3
msgid "is invalid"
msgstr ""
-## From Ecto.Changeset.validate_format/3
msgid "has invalid format"
msgstr ""
-## From Ecto.Changeset.validate_subset/3
msgid "has an invalid entry"
msgstr ""
-## From Ecto.Changeset.validate_exclusion/3
msgid "is reserved"
msgstr ""
-## From Ecto.Changeset.validate_confirmation/3
msgid "does not match confirmation"
msgstr ""
-## From Ecto.Changeset.no_assoc_constraint/3
msgid "is still associated to this entry"
msgstr ""
msgid "are still associated to this entry"
msgstr ""
-## From Ecto.Changeset.validate_length/3
msgid "should be %{count} character(s)"
msgid_plural "should be %{count} character(s)"
msgstr[0] ""
@@ -70,7 +67,6 @@
msgstr[0] ""
msgstr[1] ""
-## From Ecto.Changeset.validate_number/3
msgid "must be less than %{count}"
msgid_plural "must be less than %{count}"
msgstr[0] ""
diff --git a/larus/priv/gettext/default.pot b/larus/priv/gettext/default.pot
new file mode 100644
--- /dev/null
+++ b/larus/priv/gettext/default.pot
@@ -0,0 +1,24 @@
+## 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 ""
+"Language: INSERT LANGUAGE HERE\n"
+
+#: web/templates/page/index.html.eex:2
+msgid "Welcome to %{name}"
+msgstr ""
+
+#: web/views/layout_view.ex:9
+msgid "Larus flying high."
+msgstr ""
+
+#: web/views/layout_view.ex:13
+msgid "[YOUR TITLE HERE]"
+msgstr ""
diff --git a/larus/priv/gettext/en/LC_MESSAGES/default.po b/larus/priv/gettext/en/LC_MESSAGES/default.po
new file mode 100644
--- /dev/null
+++ b/larus/priv/gettext/en/LC_MESSAGES/default.po
@@ -0,0 +1,23 @@
+## `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"
+
+#: web/templates/page/index.html.eex:2
+msgid "Welcome to %{name}"
+msgstr ""
+
+#: web/views/layout_view.ex:9
+msgid "Larus flying high."
+msgstr ""
+
+#: web/views/layout_view.ex:13
+msgid "[YOUR TITLE HERE]"
+msgstr ""
diff --git a/larus/priv/gettext/errors.pot b/larus/priv/gettext/errors.pot
--- a/larus/priv/gettext/errors.pot
+++ b/larus/priv/gettext/errors.pot
@@ -3,7 +3,6 @@
## 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.
-
## From Ecto.Changeset.cast/4
msgid "can't be blank"
msgstr ""
diff --git a/larus/priv/gettext/nl/LC_MESSAGES/default.po b/larus/priv/gettext/nl/LC_MESSAGES/default.po
new file mode 100644
--- /dev/null
+++ b/larus/priv/gettext/nl/LC_MESSAGES/default.po
@@ -0,0 +1,23 @@
+## `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: nl\n"
+
+#: web/templates/page/index.html.eex:2
+msgid "Welcome to %{name}"
+msgstr ""
+
+#: web/views/layout_view.ex:9
+msgid "Larus flying high."
+msgstr ""
+
+#: web/views/layout_view.ex:13
+msgid "[YOUR TITLE HERE]"
+msgstr "[UW TITEL HIER]"
diff --git a/larus/priv/gettext/errors.pot b/larus/priv/gettext/nl/LC_MESSAGES/errors.po
copy from larus/priv/gettext/errors.pot
copy to larus/priv/gettext/nl/LC_MESSAGES/errors.po
--- a/larus/priv/gettext/errors.pot
+++ b/larus/priv/gettext/nl/LC_MESSAGES/errors.po
@@ -1,45 +1,42 @@
-## 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`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: nl\n"
-## From Ecto.Changeset.cast/4
msgid "can't be blank"
msgstr ""
-## From Ecto.Changeset.unique_constraint/3
msgid "has already been taken"
msgstr ""
-## From Ecto.Changeset.put_change/3
msgid "is invalid"
msgstr ""
-## From Ecto.Changeset.validate_format/3
msgid "has invalid format"
msgstr ""
-## From Ecto.Changeset.validate_subset/3
msgid "has an invalid entry"
msgstr ""
-## From Ecto.Changeset.validate_exclusion/3
msgid "is reserved"
msgstr ""
-## From Ecto.Changeset.validate_confirmation/3
msgid "does not match confirmation"
msgstr ""
-## From Ecto.Changeset.no_assoc_constraint/3
msgid "is still associated to this entry"
msgstr ""
msgid "are still associated to this entry"
msgstr ""
-## From Ecto.Changeset.validate_length/3
msgid "should be %{count} character(s)"
msgid_plural "should be %{count} character(s)"
msgstr[0] ""
@@ -70,7 +67,6 @@
msgstr[0] ""
msgstr[1] ""
-## From Ecto.Changeset.validate_number/3
msgid "must be less than %{count}"
msgid_plural "must be less than %{count}"
msgstr[0] ""
diff --git a/larus/web/controllers/page_controller.ex b/larus/web/controllers/page_controller.ex
--- a/larus/web/controllers/page_controller.ex
+++ b/larus/web/controllers/page_controller.ex
@@ -1,7 +1,7 @@
defmodule Larus.PageController do
- use Larus.Web, :controller
+ use Larus.Web, :controller
- def index(conn, _params) do
- render conn, "index.html"
- end
+ def index(conn, _params) do
+ render conn, "index.html"
+ end
end
diff --git a/larus/web/gettext.ex b/larus/web/gettext.ex
--- a/larus/web/gettext.ex
+++ b/larus/web/gettext.ex
@@ -1,24 +1,36 @@
defmodule Larus.Gettext do
- @moduledoc """
- A module providing Internationalization with a gettext-based API.
+ @moduledoc """
+ A module providing Internationalization with a gettext-based API.
- By using [Gettext](http://hexdocs.pm/gettext),
- your module gains a set of macros for translations, for example:
+ By using [Gettext](http://hexdocs.pm/gettext),
+ your module gains a set of macros for translations, for example:
- import Larus.Gettext
+ import Larus.Gettext
- # Simple translation
- gettext "Here is the string to translate"
+ # Simple translation
+ gettext "Here is the string to translate"
- # Plural translation
- ngettext "Here is the string to translate",
- "Here are the strings to translate",
- 3
+ # Plural translation
+ ngettext "Here is the string to translate",
+ "Here are the strings to translate",
+ 3
- # Domain-based translation
- dgettext "errors", "Here is the error message to translate"
+ # Domain-based translation
+ dgettext "errors", "Here is the error message to translate"
- See the [Gettext Docs](http://hexdocs.pm/gettext) for detailed usage.
- """
- use Gettext, otp_app: :larus
+ See the [Gettext Docs](http://hexdocs.pm/gettext) for detailed usage.
+ """
+ use Gettext, otp_app: :larus
+
+ def supported_locales do
+ known = Gettext.known_locales(Larus.Gettext)
+ allowed = config[:locales]
+
+ Set.intersection(Enum.into(known, HashSet.new), Enum.into(allowed, HashSet.new))
+ |> Set.to_list
+ end
+
+ defp config do
+ Application.get_env(:larus, __MODULE__)
+ end
end
diff --git a/larus/web/router.ex b/larus/web/router.ex
--- a/larus/web/router.ex
+++ b/larus/web/router.ex
@@ -7,6 +7,7 @@
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
+ plug Larus.Plug.Locale, "en"
end
pipeline :api do
diff --git a/larus/web/templates/layout/app.html.eex b/larus/web/templates/layout/app.html.eex
--- a/larus/web/templates/layout/app.html.eex
+++ b/larus/web/templates/layout/app.html.eex
@@ -1,35 +1,22 @@
<!DOCTYPE html>
-<html lang="en">
- <head>
- <meta charset="utf-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <meta name="description" content="">
- <meta name="author" content="">
+<!--[if lt IE 9]> <html class="lt-ie9"> <![endif]-->
+<!--[if gt IE 8]><!--> <html lang="<%= assigns[:locale] %>"> <!--<![endif]-->
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="description" content="<%= description %>">
+ <meta name="author" content="<%= author %>">
+ <title><%= title %></title>
+ </head>
- <title>Hello Larus!</title>
- <link rel="stylesheet" href="<%= static_path(@conn, "/css/app.css") %>">
- </head>
+ <body>
+ <div id="space">
+ <%= render @view_module, @view_template, assigns %>
+ </div>
- <body>
- <div class="container">
- <header class="header">
- <nav role="navigation">
- <ul class="nav nav-pills pull-right">
- <li><a href="http://www.phoenixframework.org/docs">Get Started</a></li>
- </ul>
- </nav>
- <span class="logo"></span>
- </header>
-
- <p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
- <p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
-
- <main role="main">
- <%= render @view_module, @view_template, assigns %>
- </main>
-
- </div> <!-- /container -->
- <script src="<%= static_path(@conn, "/js/app.js") %>"></script>
- </body>
+ <link rel="stylesheet" href="<%= static_path(@conn, "/css/app.css") %>">
+ <script src="<%= static_path(@conn, "/js/app.js") %>"></script>
+ </body>
</html>
diff --git a/larus/web/templates/page/index.html.eex b/larus/web/templates/page/index.html.eex
--- a/larus/web/templates/page/index.html.eex
+++ b/larus/web/templates/page/index.html.eex
@@ -1,36 +1,2 @@
-<div class="jumbotron">
- <h2><%= gettext "Welcome to %{name}", name: "Phoenix!" %></h2>
- <p class="lead">A productive web framework that<br />does not compromise speed and maintainability.</p>
-</div>
-
-<div class="row marketing">
- <div class="col-lg-6">
- <h4>Resources</h4>
- <ul>
- <li>
- <a href="http://phoenixframework.org/docs/overview">Guides</a>
- </li>
- <li>
- <a href="http://hexdocs.pm/phoenix">Docs</a>
- </li>
- <li>
- <a href="https://github.com/phoenixframework/phoenix">Source</a>
- </li>
- </ul>
- </div>
-
- <div class="col-lg-6">
- <h4>Help</h4>
- <ul>
- <li>
- <a href="http://groups.google.com/group/phoenix-talk">Mailing list</a>
- </li>
- <li>
- <a href="http://webchat.freenode.net/?channels=elixir-lang">#elixir-lang on freenode IRC</a>
- </li>
- <li>
- <a href="https://twitter.com/elixirphoenix">@elixirphoenix</a>
- </li>
- </ul>
- </div>
+<div id="view">
</div>
diff --git a/larus/web/views/layout_view.ex b/larus/web/views/layout_view.ex
--- a/larus/web/views/layout_view.ex
+++ b/larus/web/views/layout_view.ex
@@ -1,3 +1,15 @@
defmodule Larus.LayoutView do
- use Larus.Web, :view
+ use Larus.Web, :view
+
+ def author do
+ "Kolab Systems AG"
+ end
+
+ def description do
+ gettext "Larus flying high."
+ end
+
+ def title do
+ gettext "[YOUR TITLE HERE]"
+ end
end
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Apr 10, 3:01 PM (1 d, 1 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18855459
Default Alt Text
D168.1775833313.diff (18 KB)
Attached To
Mode
D168: Inline localization
Attached
Detach File
Event Timeline