You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user-java@ibatis.apache.org by Collin Peters <ca...@gmail.com> on 2007/06/29 19:03:39 UTC

I18N best practices with iBatis

I have some I18N questions and am wondering what others do out there.

We have an I18N system setup where there is a database table storing
all strings and their translations.  There is tags against each i18n
string and then they will get written out different files (some
.properties for java, some .xml for flash/flex, some .php for php
etc...).  We also do some queries where we do a join against this
table to keep things simple at the database level.  How this looks is:

SELECT a.activity_id, i."en_US" as name
FROM  activities a
JOIN  i18n i ON (a.name_i18n_id = i.i18n_id)

This means that we have to pass in the locale (en_US) to the query.
In PHP, Perl, and straight Java this isn't a problem.  But how would
this easily be accomplished with iBatis?  Do I have to pass in the
locale as a parameter to *every* query that supports it?  Would there
be some way of making this automatic and transparent?

Regards,
Collin Peters

Re: I18N best practices with iBatis

Posted by Larry Meadors <lm...@apache.org>.
OK, so here's what I'd do.

Initialize one sqlmapclient instance per locale, pass in the locale as
a property when you initialize it, and use the property notation
${locale} in your sql maps to substitute the value when the sql map is
created.

Create a ThreadLocalSqlMapFactory:
===
public class ThreadLocalSqlMapFactory {
	private static ThreadLocal<SqlMapClient> threadLocal = new
ThreadLocal<SqlMapClient>();
	public static SqlMapClient getClient(){
		return threadLocal.get();
	}
	public static void setClient(SqlMapClient client){
		threadLocal.set(client);
	}
}
===

Create a Filter that looks at the current user's session, if there is
no locale there, set it to the default. If there is a locale there,
get it. In either case, at this point the user's session has a locale
on it. Use that to look up the sqlmapclient for that locale and set it
on the ThreadLocalSqlMapFactory (above). Call the filter chain in a
try block, and in the finally, set the ThreadLocalSqlMapFactory's
SqlMapClient property to null.

Now, anywhere in your code that you need a SqlMapClient, you can call
the static method to get the one that is stored on the current thread.

You could probably use a proxy to make it even more transparent (and I
think you could even plug it in with Spring), but you'd have to hire
me for a few hours to get me to write that code for you.

Email me off-list if you are interested in doing that. :-D

Larry


On 7/5/07, Collin Peters <ca...@gmail.com> wrote:
> web app.  Frontend is built in Flex.
>
> On 7/5/07, Larry Meadors <lm...@apache.org> wrote:
> > Is this a web app or a desktop app?
> >
> > Larry
> >
> >
> > On 7/4/07, Collin Peters <ca...@gmail.com> wrote:
> > > On 6/29/07, Larry Meadors <lm...@apache.org> wrote:
> > > > So you have a field for each locale?
> > >
> > > Yes, a column for each locale in the i18n table
> > >
> > > > I'd think it would be easier to if that were a compound key instead:
> > > >
> > > > SELECT a.activity_id, i.name as name
> > > > FROM  activities a
> > > > JOIN  i18n i ON (a.name_i18n_id = i.i18n_id and i.i18n_locale = #locale#)
> > > >
> > > > But.. I guess you'd still have to pass it to each query...
> > >
> > > Yup, same question for iBatis
> > >
> > > > How about this - if you don't have a ton of locales to support, you
> > > > could do this:
> > > >
> > > > SELECT a.activity_id, i.${locale} as name
> > > > FROM  activities a
> > > > JOIN  i18n i ON (a.name_i18n_id = i.i18n_id)
> > > >
> > > > Then pass in the locale when you create the sqlmap client - each
> > > > locale would have it's own sql map client that you could put in a Map,
> > > > then put on the thread (use a ThreadLocale) and get from there when
> > > > needed. That could be a pretty clean solution if done well.
> > >
> > > Can you expand on this idea a bit?  I'm not sure what you mean with
> > > the Thread idea.  In the end all I want is a way to abstract having to
> > > send the locale as a variable to *every* single call in my iBatis
> > > code.
> > >
> >
>

Re: I18N best practices with iBatis

Posted by Collin Peters <ca...@gmail.com>.
web app.  Frontend is built in Flex.

On 7/5/07, Larry Meadors <lm...@apache.org> wrote:
> Is this a web app or a desktop app?
>
> Larry
>
>
> On 7/4/07, Collin Peters <ca...@gmail.com> wrote:
> > On 6/29/07, Larry Meadors <lm...@apache.org> wrote:
> > > So you have a field for each locale?
> >
> > Yes, a column for each locale in the i18n table
> >
> > > I'd think it would be easier to if that were a compound key instead:
> > >
> > > SELECT a.activity_id, i.name as name
> > > FROM  activities a
> > > JOIN  i18n i ON (a.name_i18n_id = i.i18n_id and i.i18n_locale = #locale#)
> > >
> > > But.. I guess you'd still have to pass it to each query...
> >
> > Yup, same question for iBatis
> >
> > > How about this - if you don't have a ton of locales to support, you
> > > could do this:
> > >
> > > SELECT a.activity_id, i.${locale} as name
> > > FROM  activities a
> > > JOIN  i18n i ON (a.name_i18n_id = i.i18n_id)
> > >
> > > Then pass in the locale when you create the sqlmap client - each
> > > locale would have it's own sql map client that you could put in a Map,
> > > then put on the thread (use a ThreadLocale) and get from there when
> > > needed. That could be a pretty clean solution if done well.
> >
> > Can you expand on this idea a bit?  I'm not sure what you mean with
> > the Thread idea.  In the end all I want is a way to abstract having to
> > send the locale as a variable to *every* single call in my iBatis
> > code.
> >
>

Re: I18N best practices with iBatis

Posted by Larry Meadors <lm...@apache.org>.
Is this a web app or a desktop app?

Larry


On 7/4/07, Collin Peters <ca...@gmail.com> wrote:
> On 6/29/07, Larry Meadors <lm...@apache.org> wrote:
> > So you have a field for each locale?
>
> Yes, a column for each locale in the i18n table
>
> > I'd think it would be easier to if that were a compound key instead:
> >
> > SELECT a.activity_id, i.name as name
> > FROM  activities a
> > JOIN  i18n i ON (a.name_i18n_id = i.i18n_id and i.i18n_locale = #locale#)
> >
> > But.. I guess you'd still have to pass it to each query...
>
> Yup, same question for iBatis
>
> > How about this - if you don't have a ton of locales to support, you
> > could do this:
> >
> > SELECT a.activity_id, i.${locale} as name
> > FROM  activities a
> > JOIN  i18n i ON (a.name_i18n_id = i.i18n_id)
> >
> > Then pass in the locale when you create the sqlmap client - each
> > locale would have it's own sql map client that you could put in a Map,
> > then put on the thread (use a ThreadLocale) and get from there when
> > needed. That could be a pretty clean solution if done well.
>
> Can you expand on this idea a bit?  I'm not sure what you mean with
> the Thread idea.  In the end all I want is a way to abstract having to
> send the locale as a variable to *every* single call in my iBatis
> code.
>

Re: I18N best practices with iBatis

Posted by Collin Peters <ca...@gmail.com>.
On 6/29/07, Larry Meadors <lm...@apache.org> wrote:
> So you have a field for each locale?

Yes, a column for each locale in the i18n table

> I'd think it would be easier to if that were a compound key instead:
>
> SELECT a.activity_id, i.name as name
> FROM  activities a
> JOIN  i18n i ON (a.name_i18n_id = i.i18n_id and i.i18n_locale = #locale#)
>
> But.. I guess you'd still have to pass it to each query...

Yup, same question for iBatis

> How about this - if you don't have a ton of locales to support, you
> could do this:
>
> SELECT a.activity_id, i.${locale} as name
> FROM  activities a
> JOIN  i18n i ON (a.name_i18n_id = i.i18n_id)
>
> Then pass in the locale when you create the sqlmap client - each
> locale would have it's own sql map client that you could put in a Map,
> then put on the thread (use a ThreadLocale) and get from there when
> needed. That could be a pretty clean solution if done well.

Can you expand on this idea a bit?  I'm not sure what you mean with
the Thread idea.  In the end all I want is a way to abstract having to
send the locale as a variable to *every* single call in my iBatis
code.

Re: I18N best practices with iBatis

Posted by Larry Meadors <lm...@apache.org>.
So you have a field for each locale?

I'd think it would be easier to if that were a compound key instead:

SELECT a.activity_id, i.name as name
FROM  activities a
JOIN  i18n i ON (a.name_i18n_id = i.i18n_id and i.i18n_locale = #locale#)

But.. I guess you'd still have to pass it to each query...

How about this - if you don't have a ton of locales to support, you
could do this:

SELECT a.activity_id, i.${locale} as name
FROM  activities a
JOIN  i18n i ON (a.name_i18n_id = i.i18n_id)

Then pass in the locale when you create the sqlmap client - each
locale would have it's own sql map client that you could put in a Map,
then put on the thread (use a ThreadLocale) and get from there when
needed. That could be a pretty clean solution if done well.

Larry


On 6/29/07, Collin Peters <ca...@gmail.com> wrote:
> I have some I18N questions and am wondering what others do out there.
>
> We have an I18N system setup where there is a database table storing
> all strings and their translations.  There is tags against each i18n
> string and then they will get written out different files (some
> .properties for java, some .xml for flash/flex, some .php for php
> etc...).  We also do some queries where we do a join against this
> table to keep things simple at the database level.  How this looks is:
>
> SELECT a.activity_id, i."en_US" as name
> FROM  activities a
> JOIN  i18n i ON (a.name_i18n_id = i.i18n_id)
>
> This means that we have to pass in the locale (en_US) to the query.
> In PHP, Perl, and straight Java this isn't a problem.  But how would
> this easily be accomplished with iBatis?  Do I have to pass in the
> locale as a parameter to *every* query that supports it?  Would there
> be some way of making this automatic and transparent?
>
> Regards,
> Collin Peters
>