taoensso / tower

i18n & L10n library for Clojure/Script
https://www.taoensso.com/tower
Eclipse Public License 1.0
278 stars 24 forks source link

What is correct way to specify deeply nested translation path? #57

Closed ul closed 9 years ago

ul commented 9 years ago

Actually, using either (t :en :a/b/c) or (t :en :a.b/c) works fine, but formally neither is correct. Keywords must be correctly namespaced in Clojure (having at most only one namespace and thus one /) but http://clojure.org/reader also bans using .:

Keywords are like symbols, except: They can and must begin with a colon, e.g. :fred. They cannot contain '.' or name classes. A keyword that begins with two colons is resolved in the current namespace: In the user namespace, ::rect is read as :user/rect

I'm confused a little bit.

ul commented 9 years ago

Oh, may be I misinterpreted docs: namespace part can contain dot, but name part cannot. Right? If so, excuse me for the noise.

ptaoussanis commented 9 years ago

Hi Ruslan,

Namespace part can definitely contain a dot. The name part containing a dot is contentious, I think: it was historically disallowed by the spec - but I know that at least ClojureScript has started officially using it in cases like js/foo.bar, etc.

Tower isn't too restrictive in what it accepts, but I'd suggest the :a.b/c form (dots in namespace okay, no dots after namespace) just to be safe. :a/b/c is definitely wrong.

Hope that helps?

ul commented 9 years ago

Yes, thank you!

JohnPostlethwait commented 8 years ago

On this subject, I couldn't get the translation path to resolve in ClojureScript unless I used the dot style. The slash style was never able to compile...

Caused by: clojure.lang.ExceptionInfo: Invalid token: :forms/new-user/title {:type :reader-exception, :line 12, :column 80, :file "[...]/client/components/user/state.cljs"}
    at clojure.core$ex_info.invoke(core.clj:4593)
    at clojure.tools.reader.reader_types$reader_error.doInvoke(reader_types.clj:330)
    at clojure.lang.RestFn.invoke(RestFn.java:439)
    at clojure.tools.reader$read_keyword.invoke(reader.clj:354)
    at clojure.tools.reader$read_STAR_.invoke(reader.clj:878)
    at clojure.tools.reader$read_delimited.invoke(reader.clj:189)
    at clojure.tools.reader$read_list.invoke(reader.clj:202)
    at clojure.tools.reader$read_STAR_.invoke(reader.clj:878)
    at clojure.tools.reader$read_delimited.invoke(reader.clj:189)
    at clojure.tools.reader$read_map.invoke(reader.clj:236)
    at clojure.tools.reader$read_STAR_.invoke(reader.clj:878)
    at clojure.tools.reader$read_delimited.invoke(reader.clj:189)
    at clojure.tools.reader$read_map.invoke(reader.clj:236)
    at clojure.tools.reader$read_STAR_.invoke(reader.clj:878)
    at clojure.tools.reader$read_delimited.invoke(reader.clj:189)
    at clojure.tools.reader$read_list.invoke(reader.clj:202)
    at clojure.tools.reader$read_STAR_.invoke(reader.clj:878)
    at clojure.tools.reader$read.invoke(reader.clj:927)
    at cljs.analyzer$forms_seq_STAR_$forms_seq___2134$fn__2135$fn__2136.invoke(analyzer.cljc:2040)
    at cljs.analyzer$forms_seq_STAR_$forms_seq___2134$fn__2135.invoke(analyzer.cljc:2034)
    at clojure.lang.LazySeq.sval(LazySeq.java:40)
    at clojure.lang.LazySeq.seq(LazySeq.java:49)
    at clojure.lang.Cons.next(Cons.java:39)
    at clojure.lang.RT.next(RT.java:674)
    at clojure.core$next__4112.invoke(core.clj:64)
    at cljs.analyzer$analyze_file$fn__2173.invoke(analyzer.cljc:2235)
    at cljs.analyzer$analyze_file.invoke(analyzer.cljc:2227)
    at cljs.analyzer$analyze_deps.invoke(analyzer.cljc:1291)
    at cljs.analyzer$eval1872$fn__1874.invoke(analyzer.cljc:1545)
    at clojure.lang.MultiFn.invoke(MultiFn.java:251)
    at cljs.analyzer$analyze_seq.invoke(analyzer.cljc:1900)
    ... 40 more

That exact path was fine when I changed it to :forms.new-user.title

I'm using 3.1.0-beta3

ptaoussanis commented 8 years ago

:forms/new-user/title

Please note that this is not a valid keyword on any platform. It may work in Clojure by accident (not sure), but it's definitely malformed by the spec. I would strongly recommend against this form even if it seems to work in some cases. Any support is accidental and may break at any time.

JohnPostlethwait commented 8 years ago

Perhaps I am confused... The README in the Translation section says to use it in that way? https://github.com/ptaoussanis/tower/blob/master/README.md#translation IE:

(t :en :example/inline-markdown)
JohnPostlethwait commented 8 years ago

Ah! I see now, after looking through the source: https://github.com/ptaoussanis/encore/blob/master/src/taoensso/encore.cljx#L576

It explodes it and uses a get-in to find the translation.

ptaoussanis commented 8 years ago

Perhaps I am confused... The README in the Translation section says to use it in that way? https://github.com/ptaoussanis/tower/blob/master/README.md#translation IE:

(t :en :example/inline-markdown)

Note that :foo/bar is a valid keyword but :foo/bar/baz is not: a keyword can have at most one / (which separates the "namespace" and "name" parts).

As far as Clojure/Script is concerned (i.e. nothing to do with Tower specifically):

:foo is a valid keyword with "foo" name and no namespace. :foo/bar is a valid keyword with "bar" name and "foo" namespace. :foo.bar/baz" is a valid keyword with "baz" name and "foo.bar" namespace. :foo/bar/baz is invalid (malformed) since it has more than one /. :foo.bar.baz is valid with "foo.bar.baz" name and no namespace.

Tower happens to ignore slashes since it doesn't care about the technical distinction between "name" and "namespace", so :foo.bar and :foo/bar will pick up the same translation key - but you may find that malformed keywordss won't even get through the Clojure/Script compiler - or may otherwise cause issues now or down the line.

Simplest thing to do if you don't want to think about this too much - just use .'s as your only separators - never any slashes.

Does that help / make sense?

JohnPostlethwait commented 8 years ago

Yes, it does – thanks a lot for the clarification! :smile:

jkkealii commented 5 years ago

@ptaoussanis your last comment would be a great thing to add in the tempura documentation. Just spent a while trying to understand why I couldn't dig very deep into my dictionary. Maybe adding an example of a key that is three or four levels deep?