Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JIT-selecting preferred Locale from accepted locales list and available locales set/map #43

Closed
vvvvalvalval opened this issue May 1, 2014 · 13 comments

Comments

@vvvvalvalval
Copy link

I find myself in a situation where I do not anticipate what locales are available to me (i.e the config is unanticipated) and I have a list of accepted locales in preference order (typically from an HTTP Accept-languages header). So I'd like to know, for a particular message, which is the best locale to choose from those that are bore available in my config and accepted. I posted on StackOverflow about this.

In other words, it'd be nice to be able to do something like

(t [:fr-FR :fr :en-US :en] my-tconfig :path/to/message)

but you can't. And I cannot add that functionality in a proper way in my own application because I'd need access to the loc-tree function which is private.

So I propose as a first step towards this to implement a preferred-lang function that may look like this :

(defn preferred-lang 
  "
accepted-locs : seq of accepted locales by preference order
available-locs : seq of the available locales in the dictionary
fallback-loc : optional, default locale if no match found

Selects the best locale given the ordered seq of accepted locales and the set of available locales."
  [accepted-locs available-locs & [fallback-loc]]
  ...
  )
(facts "About preferred-lang"
       (preferred-lang [:fr :en :de] #{:fr :en}) => :fr

       ;; here we see the necessity of loc-tree
       (preferred-lang [:fr-FR :en :de] #{:fr :en}) => :fr 

       (preferred-lang [:fr :en :de] #{:en :de}) => :en

       (preferred-lang [:fr] #{:en} :de) => :de
       )

What do you think? Shall I give it a try and make a pull request from it?

@ptaoussanis
Copy link
Member

Hi Valentin,

typically from an HTTP Accept-languages header

Sure. Note that taoensso.tower.utils/parse-http-accept-header may be useful, in case you weren't aware that it's there (it's used for the default Ring middleware).

And I cannot add that functionality in a proper way in my own application because I'd need access to the loc-tree function which is private.

The loc-tree fn just splits a locale into constituent parts. If I've understood what you'd like to do correctly, most of the relevant fallback logic is actually in make-t-uncached.

What do you think? Shall I give it a try and make a pull request from it?

What you're trying to do seems quite reasonable, and would be difficult with Tower currently - so in principle I am on board with adding support if we can do it in a way that's sensible...

Let me think about this a little and I'll get back to you!

Cheers :-)

@vvvvalvalval
Copy link
Author

Thanks for the walkthrough, I'd been looking for parse-http-request-header.

Anyway, I'd be glad to contribute, let me know if I can help :).

@ptaoussanis
Copy link
Member

However, I couldn't find make-t-uncached anywhere... what have I missed?

There's a large update pending on the dev branch. Any work on this will have to target that.

There's some breaking API changes, so I've been reluctant to merge into master until I'm sure I'm satisfied with the changes.

Anyway, I'd be glad to contribute, let me know if I can help.

I appreciate that, thank you. Am actually experimenting with this right now. It might be possible to get this working with only a minor change, but I'd like to confirm. Will definitely come back to you if there's something you could assist with.

@ptaoussanis
Copy link
Member

Okay, I've updated v2.1.0-SNAPSHOT so that translation fns can now take a vector of descending-preference locales. 46f21ce

These'll be searched intelligently. From the updated README:

And even fallback locales. `(t [:fr-FR :en-US] :example/foo)` searches:
  1. `:example/foo` in the `:fr-FR` locale.
  2. `:example/foo` in the `:en-US` locale.
  3. `:example/foo` in the `:fr` locale.
  4. `:example/foo` in the `:en` locale.
  5. `:example/foo` in the fallback locale.
  6. `:missing` in any of the above locales.

This works in both Clojure and ClojureScript versions of Tower.

Also modified the Ring middleware to use all Accept-Language header languages in this way: dictionaries will automatically be searched for translation entries intelligently.

Haven't tested this much (esp. the middleware), so feedback and/or bug reports welcome.

Cheers! :-)

@vvvvalvalval
Copy link
Author

Thank you, but I think the preference order is not quite right. In your example, I believe 1-3-2-4-5-6 is more relevant (If you value :fr-Fr more than :en-US, then you probably prefer :fr to :en-US too. That's my use case at least).

The examples in my first post can give you an idea of what I have in mind, if you want I can come up with a more complete one.

Cheers,

@ptaoussanis
Copy link
Member

Sure, fixed: 244e082

@ptaoussanis
Copy link
Member

Note that this search strategy does handle some forms of Accept-Language a bit strangely. For example, a ["en-GB" "en-US" "en"] browser preference will become a [:en-GB :en :en-US] locale preference.

I don't personally think that's a big issue though, since the Accept-Language choices are rarely intentionally specific to that extent.

@vvvvalvalval
Copy link
Author

That's what I was thinking about when I said "more complete example", and indeed I think it's an issue. But I agree it's not an emergency, I suggest to file it as an improvement and take the time to think about it.

@vvvvalvalval
Copy link
Author

Also, if we go down that road, that suggests changing the Ring middleware too, don't you think?

@ptaoussanis
Copy link
Member

But I agree it's not an emergency, I suggest to file it as an improvement and take the time to think about it.

Sure.

Also, if we go down that road, that suggests changing the Ring middleware too, don't you think?

I don't follow, sorry?

@vvvvalvalval
Copy link
Author

Well, for example we could make the Ring middleware attach to the request a translation (partial) function that uses the Locales list extracted from its accept-language header.

From what I understand, the current Ring middleware selects "blindly" a best locale for the translations that will result from the request, and attaches a partial translation function for that purpose.

But that new implementation of t allows for a more pessimistic behavior, which is using the whole sequence of accepted as a partial argument, i.e something like

(handler (assoc request :locale (tower/locale-key loc)
                                :tconfig tconfig
                                :t (if tconfig
                                     (partial tower/t accepted-locs tconfig)
                                     (partial tower/t accepted-locs))))

in taoensso.tower.ring/wrap-tower-middleware.

@ptaoussanis
Copy link
Member

From what I understand, the current Ring middleware selects "blindly" a best locale for the translations that will result from the request, and attaches a partial translation function for that purpose.

Ahh, gotcha. Yes, I actually made the change as part of the earlier 2.1.0 snapshot:
0265b7d :-)

All the commits on the dev branch are here: https://github.com/ptaoussanis/tower/commits/dev

@vvvvalvalval
Copy link
Author

My mistake, I wasn't looking at the right place. Fine then:)
Thanks very much for reacting so quickly on this.
Bests,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants