Translation Service. Please, help me improve it ... Thank You

S

Shapper

Hello,

I am creating a translation service to use on application.

My idea is to setup the translations as follows:

public class MsgTranslatorProvider : TranslatorProviderBase {

public MsgTranslatorProvider() {
For("Hello", "en").Add(new Dictionary<String, String> { { "Bonjour", "fr" }, { "Olá", "pt" } });

}
}

Note that "en" version is the main version ...

Then on my application I would simply request a translation:

_translator.Get("Hello", "pt");

I have this working but I would like suggestions to improve it:

public interface ITranslatorService {
String Get(String text, String culture);
} // ITranslatorService

public class TranslatorService : ITranslatorService {
public String Get(String text, String culture) {
return TranslatorStore.Get(text, culture);
} // Get
} // TranslatorService

public class TranslatorStore {
private static IList<TranslatorProviderBase> _providers = new List<TranslatorProviderBase>();

public static void AddProvider<T>() where T : TranslatorProviderBase, new() {
_providers.Add(new T());
} // AddProvider

public static void AddProvider(TranslatorProviderBase provider) {
foreach (TranslatorProviderBase _provider in _providers)
if (_provider.Translations.Keys.Intersect(provider.Translations.Keys).Count() > 0)
throw new ArgumentException("Duplicate texts where found in the providers");
_providers.Add(provider);
} // AddProvider

public static String Get(String text, String culture) {
foreach (TranslatorProviderBase provider in _providers) {
IDictionary<String, String> translation;
if (provider.Translations.TryGetValue(text, out translation))
return translation[culture];
}
return String.Empty;
} // Get

} // TranslatorStore

public class TranslatorProviderBase {
public TranslatorDictionary Translations { get; private set; }

public TranslatorProviderBase() {
Translations = new TranslatorDictionary();
} // TranslatorProviderBase

public TranslatorBuilder For(String text, String culture) {

if (Translations.ContainsKey(text))
throw new ArgumentException(String.Format("A translation of text '{0}' already exists"));
else
Translations.Add(text, new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase) { { culture, text } });

return new TranslatorBuilder(text, Translations);
} // For

} // TranslatorProviderBase

public class TranslatorBuilder {

private readonly String _key;
private readonly TranslatorDictionary _translations;

public TranslatorBuilder(String key, TranslatorDictionary translations){
_key = key;
_translations = translations;
} // TranslatorBuilder

public TranslatorBuilder Add(String text, String culture) {
_translations[_key].Add(culture, text);
return this;
} // Add

public TranslatorBuilder Add(Dictionary<String, String> translations) {
foreach (KeyValuePair<String, String> translation in translations)
_translations[_key].Add(translation.Value, translation.Key);
return this;
} // Add

} // TranslatorBuilder

public class TranslatorDictionary : Dictionary<String, IDictionary<String, String>> { } // TranslatorDictionary


Questions:

1 - In TranslationBuilder I am using StringComparer.OrdinalIgnoreCasewhen adding a new dictionary.

But how to use StringComparer.OrdinalIgnoreCase in all dictionaries, e.g, in TranslatorDictionary.

2 - TranslatorStore is used to hold all translation providers. Should I name it this way?

3 - TranslatorStore has a few static fields an methods.

The translations should be defined only once and not every time a translation is requested.

I am not sure if this is the correct way to do it.

4 - Should TranslatorStore has a Get method to get a translator? Or only toget a provider?

5 - Should I have a Factory for TranslatorService?
I will be injecting ITranslatorService in my application. Then I will use it as follows:
_translator.Get("Hello", "pt");


I am felling something better could be done to store TranslationProviders and wire them to the TranslationService.

Anyway, any help or suggestion to improve this is welcome.
 
S

Shapper

Here you go:

http://www.microsofttranslator.com/dev/



Don't reinvent the wheel.

I tried a few translations and needed just 2 or 3 trials to get a bad translation from Portuguese to English.

I admit it could be something useful in the future but right now I can relyon it.

Can the API be used with my own translations?

Anyway, any suggestion to improve my own code is welcome.

Thank you,
Miguel
 
A

Arne Vajhøj

I am creating a translation service to use on application.

My idea is to setup the translations as follows:

public class MsgTranslatorProvider : TranslatorProviderBase {

public MsgTranslatorProvider() {
For("Hello", "en").Add(new Dictionary<String, String> { { "Bonjour", "fr" }, { "Olá", "pt" } });

}
}

Note that "en" version is the main version ...

Then on my application I would simply request a translation:

_translator.Get("Hello", "pt");

I have this working but I would like suggestions to improve it:

public interface ITranslatorService {
String Get(String text, String culture);
} // ITranslatorService

public class TranslatorService : ITranslatorService {
public String Get(String text, String culture) {
return TranslatorStore.Get(text, culture);
} // Get
} // TranslatorService

public class TranslatorStore {
private static IList<TranslatorProviderBase> _providers = new List<TranslatorProviderBase>();

public static void AddProvider<T>() where T : TranslatorProviderBase, new() {
_providers.Add(new T());
} // AddProvider

public static void AddProvider(TranslatorProviderBase provider) {
foreach (TranslatorProviderBase _provider in _providers)
if (_provider.Translations.Keys.Intersect(provider.Translations.Keys).Count() > 0)
throw new ArgumentException("Duplicate texts where found in the providers");
_providers.Add(provider);
} // AddProvider

public static String Get(String text, String culture) {
foreach (TranslatorProviderBase provider in _providers) {
IDictionary<String, String> translation;
if (provider.Translations.TryGetValue(text, out translation))
return translation[culture];
}
return String.Empty;
} // Get

} // TranslatorStore

public class TranslatorProviderBase {
public TranslatorDictionary Translations { get; private set; }

public TranslatorProviderBase() {
Translations = new TranslatorDictionary();
} // TranslatorProviderBase

public TranslatorBuilder For(String text, String culture) {

if (Translations.ContainsKey(text))
throw new ArgumentException(String.Format("A translation of text '{0}' already exists"));
else
Translations.Add(text, new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase) { { culture, text } });

return new TranslatorBuilder(text, Translations);
} // For

} // TranslatorProviderBase

public class TranslatorBuilder {

private readonly String _key;
private readonly TranslatorDictionary _translations;

public TranslatorBuilder(String key, TranslatorDictionary translations) {
_key = key;
_translations = translations;
} // TranslatorBuilder

public TranslatorBuilder Add(String text, String culture) {
_translations[_key].Add(culture, text);
return this;
} // Add

public TranslatorBuilder Add(Dictionary<String, String> translations) {
foreach (KeyValuePair<String, String> translation in translations)
_translations[_key].Add(translation.Value, translation.Key);
return this;
} // Add

} // TranslatorBuilder

public class TranslatorDictionary : Dictionary<String, IDictionary<String, String>> { } // TranslatorDictionary

I strongly suspect that this code is overly complex.

You seems to only need For, Add and Get methods.

What does all these classes provide to you?

Arne
 
S

Shapper

I am creating a translation service to use on application.

My idea is to setup the translations as follows:

public class MsgTranslatorProvider : TranslatorProviderBase {

public MsgTranslatorProvider() {
For("Hello", "en").Add(new Dictionary<String, String> { { "Bonjour", "fr" }, { "Olá", "pt" } });




Note that "en" version is the main version ...

Then on my application I would simply request a translation:

_translator.Get("Hello", "pt");

I have this working but I would like suggestions to improve it:

public interface ITranslatorService {
String Get(String text, String culture);
} // ITranslatorService

public class TranslatorService : ITranslatorService {
public String Get(String text, String culture) {
return TranslatorStore.Get(text, culture);
} // TranslatorService

public class TranslatorStore {
private static IList<TranslatorProviderBase> _providers = new List<TranslatorProviderBase>();

public static void AddProvider<T>() where T : TranslatorProviderBase, new() {
_providers.Add(new T());
} // AddProvider

public static void AddProvider(TranslatorProviderBase provider) {
foreach (TranslatorProviderBase _provider in _providers)
if (_provider.Translations.Keys.Intersect(provider.Translations.Keys).Count() > 0)
throw new ArgumentException("Duplicate texts where found in the providers");

} // AddProvider

public static String Get(String text, String culture) {
foreach (TranslatorProviderBase provider in _providers) {
IDictionary<String, String> translation;
if (provider.Translations.TryGetValue(text, out translation))
return translation[culture];

return String.Empty;
} // Get

} // TranslatorStore

public class TranslatorProviderBase {
public TranslatorDictionary Translations { get; private set; }

public TranslatorProviderBase() {
Translations = new TranslatorDictionary();
} // TranslatorProviderBase

public TranslatorBuilder For(String text, String culture) {

if (Translations.ContainsKey(text))
throw new ArgumentException(String.Format("A translation of text '{0}' already exists"));

Translations.Add(text, new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase) { { culture, text } });

return new TranslatorBuilder(text, Translations);
} // For

} // TranslatorProviderBase

public class TranslatorBuilder {

private readonly String _key;
private readonly TranslatorDictionary _translations;

public TranslatorBuilder(String key, TranslatorDictionary translations) {
_key = key;
_translations = translations;
} // TranslatorBuilder

public TranslatorBuilder Add(String text, String culture) {
_translations[_key].Add(culture, text);
return this;
} // Add

public TranslatorBuilder Add(Dictionary<String, String> translations) {
foreach (KeyValuePair<String, String> translation in translations)
_translations[_key].Add(translation.Value, translation.Key);
return this;
} // Add

} // TranslatorBuilder

public class TranslatorDictionary : Dictionary<String, IDictionary<String, String>> { } // TranslatorDictionary



I strongly suspect that this code is overly complex.



You seems to only need For, Add and Get methods.



What does all these classes provide to you?

Let me explain:

TranslatorBuilder
Helper to allow defining the translations in a Fluent Way.

TranslatorProviderBase
I would like to organize the translations in different classes.
So I might have MsgTranslator, FormTranslator, ...
All them have the same base classe: TranslatorProviderBase.

TranslatorStore:
This is just a class to wrap all translation providers.

TranslatorService:
This one is what I use to request translations.
Basically, it will look in each provider in TranslatorStore for a translation with that key.

Note that I am using a different approach to NET Resources.
I am defining a default language which value is used as key.

So instead of using:
Resources.ErrorMessage

I use:
translator.Get("This is an error")

I see immediately what I am translating.

Thank You,
Miguel
 
S

Shapper

Translation quality depends on the amount of input data used to build the

systems. I guess the Portuguese data is insufficient.






See this announcment:

http://blogs.msdn.com/b/translation...rosoft-translator-hub-for-commercial-use.aspx



If you can provide your own input data, the Translator service will provide

custom translations.



Unfortunately, you're extremely vague about what sort of translation you're

trying to do. If you want human language translation on arbitrary input,

then you'll need a real translator and should probably explore Microsoft's

Translator Hub feature.



If you only want to translate precise words, then it's as simple as having

a dictionary-based setup. How those dictionaries are configured depend on

your needs. If your input language is always the same, then you just need

a dictionary mapping from language name to another dictionary, where that

other dictionary has the mappings from the input language words to the

output language words. If you want to translate between arbitrary

languages, you'll need an extra indirection of dictionaries.



If that's what you're doing, then I agree with Arne that you don't need

much in the way of custom API. It would probably be nice to wrap the

dictionaries in a helper method that takes the input word, output language,

and (if needed) input language, but otherwise it's just a matter of looking

things up in the dictionaries.



Of course, if all you're doing is translating the UI in your program, then

a much better way to do that is to use the .NET localization features.

Then the code just uses resource names, and the appropriate localized

resources are loaded for display purposes.



It all really just depends on what you're actually doing, and since you

haven't explained that, there's no way to know what you really need to do..



Pete

Hello Pete,

At the moment I need translation for precise words and phrases.
Yes, I could use Resources ... But I prefer not to.
In a complex application they tend to be confusing.

I would like to have my own independent Translator API.

But in the future I might plug Microsoft Translator or Google Translator init ...

It would be as simple as when the translation is not present in my providers then use one of the mentioned above.

In some parts of my application I really need precise translation.

Any suggestions to improve it are welcome.

Any question please let me know.

Thank You,
Miguel
 
S

Shapper

Are any of these "translators" going to translate something other than an

actual string? If not, then what's the point? Why not just a single

implementation that translates strings?







Huh? What's the point of that? You just want to add complexity to your

code for the sake of complexity?









And that's different from setting the current culture for a thread, how?







If you give your resources proper names, you always still see what you are

"translating". It's just a resource name instead of a string literal.

Hello Pete,

I started with Text translation as a start.

I will try to provide an idea of what my objectives are.

OBJECTIVE 1 - Strings

1 - translator.Translate("Hello");
2 - translator.Translate("Hello", "pt");


On (1) "Hello" would be translated to current Thread Culture.
On (2) the culture is specified so translated to "PT".

NOTES:

- Between (1) and (2) I only need to check culture for null.

- I would specify a Default Language. Basically, this is what I am doing:
For("Hello World", "en").Add(new Dictionary<String, String> { { "Bonjour", "fr" }, { "Olá", "pt" } });

- I could also use a TranslationService (Google, MSFT, ...) as an option. It would something that I would plug ...
I could use, for example, a MSFTTranslatorAdapter.

- When I use a few TranslatorProviders to separate translations is just nota matter of organization. It also allows to have in each assembly my own translation class and then join all them in the main project that uses them.


OBJECTIVE 2 - Objects

1 - translator.Translate(post);
2 - translator.Translate(posts);

"posts" is a List<Post> and post a Post where:

public class Post {
public String Title { get; set; }
public String Category { get; set; }
public String Body { get; set; }
public String Author { get; set; }
public String Tag { get; set; }
}

Then I could set a PostTranslator<Post> with rules (Example):

1 - Title > Translate from Translation Service (Google, MSFT, ...)
2 - Category > Precise Translation from my "FOR" rules)
3 - Body > Translate from Translation Service (Google, MSFT, ...)
4 - Author > Do not translate
5 - Tag > Use Precise Translation (FOR) if not found then use Translation Service (Google, MSFT, ...)


This approach is something easy to integrate into my projects.

I would like an integrated approach is flexible enough to allow all this.

I am not against Resouces but for what I am trying to do is impossible.

Thank You,
Miguel
 
A

Arne Vajhøj

Let me explain:

TranslatorBuilder
Helper to allow defining the translations in a Fluent Way.

TranslatorProviderBase
I would like to organize the translations in different classes.
So I might have MsgTranslator, FormTranslator, ...
All them have the same base classe: TranslatorProviderBase.

TranslatorStore:
This is just a class to wrap all translation providers.

TranslatorService:
This one is what I use to request translations.
Basically, it will look in each provider in TranslatorStore for a translation with that key.

I counted 6 classes and 1 interface in the code your posted.

You could do it with just 1 class with the core functionality.

If you want the Fluent style interface then you can add 1 more class
for that.

If you want to to allow for DI and different implementations
then you can add 1 interface (and change the Fluent style extensions
class to use the interface).

That is 2 classes and 1 interface.

I don't see any point in "I would like to organize the translations in
different classes. So I might have MsgTranslator, FormTranslator".

I see those a different instances of the same class not different
classes.

Arne
 
S

Shapper

I counted 6 classes and 1 interface in the code your posted.



You could do it with just 1 class with the core functionality.



If you want the Fluent style interface then you can add 1 more class

for that.



If you want to to allow for DI and different implementations

then you can add 1 interface (and change the Fluent style extensions

class to use the interface).



That is 2 classes and 1 interface.

2 Classes and 1 interface including DI and Fluent.

Please, show me how ... I have no idea.

I have been trying to improve this code and this is the minimum I was able to get.

I don't see any point in "I would like to organize the translations in

different classes. So I might have MsgTranslator, FormTranslator".



I see those a different instances of the same class not different

classes.

Thank You,
Miguel
 
A

Arne Vajhøj

2 Classes and 1 interface including DI and Fluent.

Please, show me how ... I have no idea.

I have been trying to improve this code and this is the minimum I was able to get.

In a previous thread I posted an example with the
core class and an extension class.

Adding an interface to that should not be that difficult.

Arne
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top