turquoiseowl / i18n

Smart internationalization for ASP.NET
Other
559 stars 156 forks source link

Multi-Tenant with i18n #133

Closed hummerpj closed 10 years ago

hummerpj commented 10 years ago

Hi Martin,

I was wondering if there was a way to use this library with a multi-tenant project. I need to customize messages based on tenant and language. I looked through the source a bit but I did not find anything that stuck out to me as an integration point.

For example, for Tenant #1 it may say "Hello World!" in en-us and "Hola mundo" for es.

And for Tenant #2 it may say "Greetings World!" for en-us and "Saludos mundo" for es.

Thanks, Patrick

raulvejar commented 10 years ago

My feeling is that this belongs in the logic of the application as you are changing the content of what you are communicating to a user based on his profile. It has not to do with localization but with user customization.

I suspect it will change in a different way than common translation and it would be a mistake to try to jam it inside the dictionary as a separate language…

From: hummerpj Sent: ‎Thursday‎, ‎April‎ ‎10‎, ‎2014 ‎3‎:‎46‎ ‎PM To: turquoiseowl/i18n

Hi Martin,

I was wondering if there was a way to use this library with a multi-tenant project. I need to customize messages based on tenant and language. I looked through the source a bit but I did not find anything that stuck out to me as an integration point.

For example, for Tenant #1 it may say "Hello World!" in en-us and "Hola mundo" for es.

And for Tenant #2 it may say "Greetings World!" for en-us and "Saludos mundo" for es.

Thanks, Patrick

— Reply to this email directly or view it on GitHub.

turquoiseowl commented 10 years ago

Your quite right @hummerpj, there isn't easy support for this. At the moment we have a single 'locale' folder under which all the translation files are stored, so I guess for starters you would need multiple such folders, one per tenant, then all the extra logic for this. Quite a lot of changes required there. I can't think of any other way to do it.

couraud commented 10 years ago

Hi, I'm trying to figure this out too. I think multiple locale folders is the way to go, as getting custom culture tags to work with the built in .net methods is difficult and confusing.

Martin - what I can't work out is when, during runtime, are the po files looked for? Is it possible to change the settings.LocaleDirectory programmatically perhaps?

Not to worried about the postbuild side of things - would just like to get a proof of concept up and running.

couraud commented 10 years ago

I'm guessing that it's this bit of code that needs to be overridden (in i18n.Domain/Concrete/POTranslationRepository.cs):

private string GetPathForLanguage(string langtag)
{
    return Path.Combine(GetAbsoluteLocaleDir(), langtag, "messages.po");
}

It would need to be something like return Path.Combine(GetAbsoluteLocaleDir(), GetClientLocaleSubDir(), langtag, "messages.po");

However, all the cache keys would need to be modified too. This doesn't sound great so I'm going back to the idea of a custom langtag, e.g. en-GB+XCLIENT. This obviously doesn't work well with the built in functions like GetCultureInfo but hopefully the way round that is not too complicated.

turquoiseowl commented 10 years ago

During runtime, the PO file for a particular language is loaded the first time a message lookup is performed for that language. It is loaded into a 'Translation' object.

If you set a breakpoint in the TextLocalizer.LoadMessagesIntoCache method then Debug a webapp that uses i18n you will get a good view of how it works.

You'll see that LoadMessagesIntoCache and other related methods take a single string langtag parameter. Probably what would be needed is for that single param to be augmented with the tenant key. That is, form a translation key something like this:

class TranslationKey {
    string tenantKey;
    string langtag;
}

and use that for the caching of the translation etc.

couraud commented 10 years ago

Do you have any views on using msgctxt to provide the tenant key? This module already supports this, so it would be 'neat'. A couple of issues off the top of my head:

turquoiseowl commented 10 years ago

What type of application are you developing? Is it other than a normal web site?

hummerpj commented 10 years ago

Using the msgctxt seems like an interesting solution but replacing the comment at runtime and editor support seems like a no go for me. Looking at the source I am not sure there is an easy way to implement this.

Also, at least for my multi-tenant solution, I would want some kind of fall-back type functionality. Meaning its found the tenant file but can't find the translation so it will fall-back to a default translation file that is shared between all tenants. Then if not found there... falls back to the original message.

The thing about i18n that intrigued me the most was how easy it was to plug into the ModelMetadata without having to customize the ModelMetadataPorvider or the ValidationModelMetadataProvider.

couraud commented 10 years ago

We are building a SaaS web app. The language and locale will be controlled by various bits of business logic and data, so actually I'm not using any of the URL filtering or storing cookies.

Currently I'm using a customised language tag in this format: en-GB+XYZTENANT, with locale folders named similarly. It works, albeit a little clumsily. Fortunately, all the changes I've had to make so far are just in the LanguageTag.cs file: modifying the regex as well as adding 'Tenant' to the Language, Script and Region identifiers and subsequent matching logic. If no Tenant message is available, the matching logic reverts back to the original Language Script and Region logic.

The advantage of this over the msgctxt I was thinking about, is that each tenant has its own .po file which can be sent to them to manually edit and send back for upload rather than it all being in one big file.

It works for now, but I'm very wary of straying too far from the i18n core!

turquoiseowl commented 10 years ago

Have you had a look at the language tag spec.? I'm wondering whether it accommodates what you are doing through 'Private Use Subtags'?

http://tools.ietf.org/html/bcp47#section-2.2.7

http://www.w3.org/International/articles/language-tags/Overview.en.php

If so, perhaps i18n should/could implement that. I did originally want to implement the full BCP47 spec. in the i18n.LanguageTag class but found an excuse to stop short by Windows/.NET not seeming to support those other subtags. But with the private use subtag, that may not be so relevant if as a rule it was stripped off before passing the langtag to any system API.

couraud commented 10 years ago

Ok - that seems like a much better idea. I've now switched it so that it's (for example) en-GB-x-XYZTENANT.

I'll carry on and if I think I get something that is robust I'll share it if you like?

turquoiseowl commented 10 years ago

Great, I'm very open to Pull Requests.

hummerpj commented 10 years ago

That is a very interesting solution. Not sure how much I can contribute at the moment but will definitely follow the progress of this.

On Wed, Apr 30, 2014 at 6:38 AM, Martin Connell notifications@github.comwrote:

Have you had a look at the language tag spec.? I'm wondering whether it accommodates what you are doing through 'Private Use Subtags'?

http://tools.ietf.org/html/bcp47#section-2.2.7

http://www.w3.org/International/articles/language-tags/Overview.en.php

If so, perhaps i18n should/could implement that. I did originally want to implement the full BCP47 spec. in the i18n.LanguageTag class but found an excuse to stop short by Windows/.NET not seeming to support those other subtags. But with the private use subtag, that may not be so relevant if as a rule it was stripped off before passing the langtag to any system API.

— Reply to this email directly or view it on GitHubhttps://github.com/turquoiseowl/i18n/issues/133#issuecomment-41782710 .

couraud commented 10 years ago

@hummerpj - you might want to take a look at the latest version. There's some info about the private-use subtag in the README. Hope you find this useful.