idi527
How to handle localisation in live view?
Copy of Using cldr with liveview · Issue #134 · elixir-cldr/cldr · GitHub
What’s the recommended way to use cldr / gettext with a liveview app? Right now I’m passign the locale into the session with Cldr.Plug.SetLocale plug and then set the locale again in the liveview’s mount/3 callback:
# in the router
plug Cldr.Plug.SetLocale,
apps: [:cldr, :gettext],
cldr: App.Cldr,
from: [:query, :accept_language]
plug :put_locale_into_session
@doc false
def put_locale_into_session(conn, _opts) do
%Cldr.LanguageTag{cldr_locale_name: locale} = conn.private.cldr_locale
put_session(conn, "locale", locale)
end
live "/some_live_path", AppLive
# in the live view
def mount(_params, %{"locale" => locale}, socket) do
App.Cldr.put_locale(locale)
# ...
end
because otherwise it doesn’t seem to work correctly with the locales being different in the ssr’ed page and when the live view mounts: in ssr the locale is correct (e.g. en):
%Cldr.LanguageTag{
canonical_locale_name: "en-Latn-US",
cldr_locale_name: "en",
extensions: %{},
gettext_locale_name: "en",
language: "en",
language_subtags: [],
language_variant: nil,
locale: %{},
private_use: [],
rbnf_locale_name: "en",
requested_locale_name: "en",
script: "Latn",
territory: "US",
transform: %{}
}
but then in live view it becomes en-001 (the default):
%Cldr.LanguageTag{
canonical_locale_name: "en-Latn-001",
cldr_locale_name: "en-001",
extensions: %{},
gettext_locale_name: nil,
language: "en",
language_subtags: [],
language_variant: nil,
locale: %{},
private_use: [],
rbnf_locale_name: "en",
requested_locale_name: "en-001",
script: "Latn",
territory: "001",
transform: %{}
}
So I wonder if anyone has used liveview cldr and how they resolved this problem and if there’s a less repetitive approach (something like plug macro would be great).
Marked As Solved
josevalim
There is no other option.
The docs linked by Chris also suggests a shared helper to be invoked on every LiveView, which is the approach I am also going with.
Also Liked
chrismccord
Folks have already well covered the options here with great advice. I wanted to point our that LV master here has a small section in the docs:
krns
For the sake of completeness: https://hexdocs.pm/phoenix_live_view/using-gettext.html
outlog
shipped first production liveview last week - I ended up wrapping everything in a liveview that holds state with locale etc - and then render pages (liveview components) depending on the action - so it’s kinda a router as well…
to make matters interesting it’s cookieless - so no passing the locale in the session.. had to do:
in my custom plug:
%{req_headers: req_headers} = conn
browser_accept_lang =
with {_key, value} <- Enum.find(req_headers, fn {key, _val} -> key == "accept-language" end),
{:ok, cldr} <- Cldr.AcceptLanguage.best_match(value, MyApp.Cldr)
do
cldr.language
else
_err -> "en"
end
conn
|> assign(:browser_accept_lang, browser_accept_lang)
|> assign(:conn_lang, Cldr.Plug.SetLocale.get_cldr_locale(conn).language)
browser_accept_lang is the browser locale - conn_lang could be different if the user is on /:locale/some_page - the diff is needed to conditionally rewrite uris with /:locale
then in root template: (remember we have no session to pass things through easily)
<meta name="waccept-lang" content="<%= assigns[:conn_lang] %>">
<meta name="browser_accept_lang" content="<%= assigns[:browser_accept_lang] %>">
in the liveview: so that the initial render is in correct lang - and then socket connects with locale params, so we also have them on “the socket” - see below:
@impl true
def mount(_params, _session, socket) do
# get js client connect params
connect_params = get_connect_params(socket)
{conn_assigns, _} = socket.private.assign_new
js_lang =
if connect_params do
Map.get(connect_params, "conn_lang")
else
nil
end
lang = Map.get(conn_assigns, :conn_lang) || js_lang || "en"
browser_accept_lang =
if connect_params do
Map.get(connect_params, "browser_accept_lang")
else
nil
end
browser_accept_lang = Map.get(conn_assigns, :browser_accept_lang) || browser_accept_lang || "en"
latest_post = MyApp.Journal.list_posts(lang) |> List.first()
{:ok,
assign(socket, navigate_counter: 0, original_lang: lang, lang: lang, browser_accept_lang: browser_accept_lang, latest_post: latest_post)}
end
in the js:
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let conn_lang = document.querySelector("meta[name='waccept-lang']").getAttribute("content")
let browser_accept_lang = document.querySelector("meta[name='browser_accept_lang']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {hooks: Hooks, params: {_csrf_token: csrfToken, conn_lang: conn_lang, browser_accept_lang: browser_accept_lang}})
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









