I'm building a WebWorks version of an Android app that's localised into 39 languages.
At the moment all the localisations are in xml files of key-value pairs, one file per language .
Each language file has about 400 lines (roughly 40k per file).
Users can change the language used in app.
What options are there in WebWorks to solve this kind of situation?
I'd be more than happy to convert the resource files into any other kind of format to make working with it a better experience on the platform.
You could store each language set in a JavaScript file that you include/load as needed. (I've converted the XML data to a "Map" since it is just key/value pairs)
e.g. (just ignore my translations... I just Googled this, I'm by no means fluent in Spanish)
//Spanish File "lang_spanish.js"
var translations = {
"lose_a_turn": "pierde un turno",
"do_not_pass_go": "huele como un camello",
"take_a_card": "tener una tarjeta de",
"you_win_the_game":"sin motocicletas en la biblioteca",
"you_can_not_move":"desbordamiento de la pila puede ser un lugar divertido"
};
In your <head> you can then have a generic script tag, that you just change the source of as needed.
e.g.
<script id="langFile" src="js/lang_english.js"></script>
When you want a different language, just remove this script from the DOM and add your new one. e.g.
function switchLang(langName){
var headTag = document.getElementsByTagName('head')[0];
var scriptFile = document.getElementById('langFile');
headTag.removeChild(scriptFile);
var newScript = document.createElement('script');
newScript.id = 'langFile';
newScript.src = 'js/lang_' + langName + '.js';
headTag.appendChild(newScript);
}
//call with:
switchLang('spanish');
The alternative would be to load all 39 languages by default... but that seems like overkill considering most will only ever want 1 or 2.
Related
I have the following script that maps printers based on AD grouping:
'---------------------------------------------------------------------------------------------
'-------------------------------MAPEAMENTO DE IMPRESSORAS-------------------------------------
on error resume next
'determines the user who just logged on
Set objSysInfo = CreateObject("ADSystemInfo")
Set WSHNetwork = CreateObject("WScript.Network")
'As soon as we tack on LDAP:// and construct an ADsPath we then bind to the user account in
'Active Directory and report back the groups the user belongs to; this can be done simply
'by enumerating the values in the MemberOf attribute.
strUserPath = "LDAP://" & Replace(objSysInfo.UserName, "/", "\/")
Set objUser = GetObject(strUserPath)
For Each objGroup in objUser.Groups
strGroupName = objGroup.CN
'---------------------------------------------------------------------------------------------
'--------------MAPEAMENTO DE IMPRESSORA DO GRUPO UM-------------------------------------------
'Mapeamento de impressoras por grupo definindo a impressora comom padrão para aquele grupo.
Select Case strGroupName
Case "GrupodoAD1"
WshNetwork.AddWindowsPrinterConnection "\\servidor\impressora1"
WshNetwork.SetDefaultPrinter "\\servidor\impressora1"
End Select
'---------------------------------------------------------------------------------------------
'---------------------------------------------------------------------------------------------
'--------------MAPEAMENTO DE IMPRESSORA DO GRUPO DOIS-----------------------------------------
'Mapeamento de impressoras por grupo definindo a impressora comom padrão para aquele grupo.
Select Case strGroupName
Case "GrupodoAD2"
WshNetwork.AddWindowsPrinterConnection "\\servidor\impressora2"
WshNetwork.SetDefaultPrinter "\\servidor\impressora2"
End Select
'---------------------------------------------------------------------------------------------
'==========Adicione seções adicionais de case para cada grupo do AD que tiver.+===============
next
Now I was asked to delete some local printers on user's computers. The above script runs on logon. The condition although states that only certain printers must be deleted (some users take computers home and we do not want to delete their home printers).
Is there a way I can accomplish this using the above existing script? How can I do that and where do I insert the information? Can I delete more than 1 printer using a single script?
For example I know I must delete:
Samsung SCX-6545X Series PCL 6
Samsung SCX-6545X Series PCL 5
Samsung SCX-6545X Series PS
If I have similar names like above, can I use that to delete any printer that matches parts of the name?
Best regards and thanks in advance for your help!
This is a script I found from somewhere once and adapted slightly to delete printers by device name. Please approach with caution, understand it, and adapt it to your needs carefully:
On Error Resume Next
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colInstalledPrinters = objWMIService.ExecQuery _
("Select * from Win32_Printer where DeviceID = 'HP LaserJet P1505'")
For Each objPrinter In colInstalledPrinters
objPrinter.Delete_
Next
I think you could replace the equals clause in the "SELECT" statement to use a "IN" so you could check all the printers and delete them in one hit. Example:
("Select * from Win32_Printer where DeviceID In ('HP LaserJet P1505', 'Printer_2', 'Printer_3')")
I have this kind of translation to make :
<a tal:attributes="href troncon/url;
title string:Cette etape fait partie du troncon ${troncon/nom}"
tal:content="troncon/nom">Canal du centre</a>
You see that I have a dynamic title attribute that I want to be translatable.
I've tried like this :
<a tal:attributes="href troncon/url;
title string:Cette etape fait partie du troncon ${troncon/nom}"
tal:content="troncon/nom"
i18n:attributes="title">Canal du centre</a>
And like this :
<a tal:attributes="href troncon/url;
title string:Cette etape fait partie du troncon ${troncon/nom}"
tal:content="troncon/nom"
i18n:attributes="title"
title="Cette etape fait partie du troncon ${nom}">Canal du centre</a>
But this doesn't work (of course).
Any ideas ?
The result of the tal:attributes call is passed literally to the translation machinery. i18n:attributes matches it's keys against what tal:attributes generates, and if there is a match, the original attribute on the element is ignored (see this I18N article on the Zope 3 wiki.
This means that the result of "Cette etape fait partie du troncon ${troncon/nom}" will be looked up for translation, requiring you to provide translations for each variation of the sentence that can be made with all possible values of troncon/nom.
To get support for proper placeholders in this string you are better off creating a message id in the code that generates the troncon structure and translate it there, presumably in your view. You need:
A message id with the placeholder
Attach the nom value to the message id
Translate this message to the currently selected language
Include the result in your troncon structure
I generally do this is one step:
from zope.i18n import translate
from zope.i18nmessageid import MessageFactory
_ = MessageFactory('yourdomain')
troncon = dict(
...
nom=nom,
nomtitre=translate(
_(u'troncon_nomtitre', default=u'Cette etape fait partie du troncon ${nom}',
mapping=dict(nom=nom)),
context=self.request)
)
Do note that you need a request for the translation function to pick the correct language.
You can always force the translation on context.translate():
tal:attributes="foobar python:context.translate(string, domain='translationdomain')"
http://collective-docs.readthedocs.org/en/latest/i18n/internationalisation.html#manually-translated-message-ids
However, this might be against all best practices.
The Problem:
I'm about to implement language localization to an already very large ipad application that was built using sencha touch wrapped in phonegap. I have the english and spanish translations in json files.
What I'm Planning on Doing:
I plan to load the json files into a sencha touch store, creating a global object. Then in each place where I call text that is displayed, i will replace the text with a call to the global object.
My question(s):
Is there an easier way to implement language localization with my
setup?
Will I run into issues with native sencha stuff (like datepickers)?
When loading/reloading language json files, will I have performance
issues (require a webview reload?, sencha object resizing issues,
etc)
edit 1 : Useful Related Info:
For those going down this road, it quickly becomes useful to write a simple phonegap plugin to get the ipad/iphone device's language setting into your javascript. This requires a plugin, which will look something like this:
Javascript :
part 1:
PhoneGap.exec("PixFileDownload.getSystemLanguage");
part 2(callback Function):
setLanguage(returnedLanguage)
{
GlobalVar.CurrentLanguage = returnedLanguage; //GloablVar.CurrentLanguage already defined
}
Objective C:
-(void)getSystemLanguage:(NSMutableArray*)paramArray withDict:(NSMutableDictionary*)options
{
/*Plugin Details
PhoneGap.exec("PixFileDownload.getSystemLanguage");
Returns Language Code
*/
NSUserDefaults* defs = [NSUserDefaults standardUserDefaults];
NSArray* languages = [defs objectForKey:#"AppleLanguages"];
NSString *language = [languages objectAtIndex:0];
NSLog(#"####### This is the language code%#",language);
NSString *jsCallBack;
jsCallBack = [NSString stringWithFormat:#"setLanguage('%#');",language];
[self.webView stringByEvaluatingJavaScriptFromString:jsCallBack];
}
edit 2: character encoding
When adding additional language characters to a sencha project (or any webview phonegap project), ensure that you have the proper encoding specified in the index file. This is the tag i needed.
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
I've finished this language localization plugin. It's not amazing, but it worked better than I originally speculated. Here are the short answers to each of the questions.
1- Is there an easier way to implement language localization with my
setup?
Not that i'm aware of. The comment by Stuart provided this link Sencha-touch localization. Use a store or a global JSON object? which had some good information on one way that you can use class overrides. I didn't like this approach, because it spread my language translations into different classes. But certainly, if you are doing something simple, or you want something that could be more powerful, perhaps you should investigate that.
2- Will I run into issues with native sencha stuff (like datepickers)?
I ended up specifically leaving "datepickers" in english for now. But everything else was really relatively easy to customize. Almost every graphical UI element can have it's text altered.
3- When loading/reloading language json files, will I have performance
issues (require a webview reload?, sencha object resizing issues,
etc).
The method that i employed (see below) worked exceptionally well in regards to performance. The one issue that you have is right when you switch the languages, you need that specific page to reload. Sencha handled resizing without any flaws, except where I had been foolish and statically set sizes.
Some of what I did was described in edits to the question. Here is a detailed overview of my solution. (warning, it's not the most elegant solution.)
Instead of using a pure JSON file, I ended up just using a javascript function. This isn't the greatest solution because it requires some minimal maintenance, but JSON parsing with phonegap/sencha isn't the best. (I get JSON files from translater's, and quickly paste into the javascript file. Takes around 2 minutes, see further explanation below).
Language.js
function setLanguage(language)
{
if(language == "en")
{
//console.log("inside if Language == en");
GlobalLanguage.CurrentLanguage = language;
GlobalLanguage.ID = {"glossary": [
{
//CONVERTED JSON
about : 'About',
checking_for_updates : 'Checking for updates...(This may take a few minutes.)'
//Any additional translations
}
]};
}
if (language == "es"){
//console.log("inside language == es");
GlobalLanguage.CurrentLanguage = language;
GlobalLanguage.ID = {"glossary": [
{
//CONVERTED JSON
about : 'Acerca de ',
checking_for_updates : 'Verificando actualizaciones... (Capas que demore algunos minutos).'
//Any additional translations
}]};
}
if (language == "pt"){
//console.log("inside language == pt");
GlobalLanguage.CurrentLanguage = language;
GlobalLanguage.ID = {"glossary": [
{
//CONVERTED JSON
about : 'Sobre',
checking_for_updates : 'Verificando se há atualizações... (pode demorar alguns minutos.)'
//Any additional translations
}]};
}
}
As you can see, this file allows for 3 languages (Portugese, English, and Spanish). After setting the language, you could access each localized string anywhere in your object. For example, if you need to access the word "about" simply use:
GlobalLanguage.ID.glossary[0]["about"]
This will access the GlobalLanguage object which will have whatever language loaded into the properties. So throughout your project, you could have these calls. However, I'd recommend taking it one step further
function langSay(languageIdentifier){
// console.log("inside langSay");
if(!GlobalLanguage.ID.glossary[0][languageIdentifier]){
return "[! LANGUAGE EXCEPTION !]";
}
else{
return GlobalLanguage.ID.glossary[0][languageIdentifier];
}
}
This protects you from having language exceptions and having your program crash without knowing where (you may have hundreds or thousands of properties being set in that language.js file). So now simply :
langSay("about")
One additional note about formatting from JSON. The format you want your language files in is:
languageIdentifier : 'Translation',
languageIdentifier : 'Translation',
languageIdentifier : 'Translation'
I used Excel for the formatting. Also the languageIdentifiers are unique identifiers without spaces. I recommend just using Excel to format first 3 to 4 words word1_word2_word3_word4 of the english translation.
word1_word2_word3 : 'word1 word2 word3'
Hope this helps you out! I'd be happy to respond to any questions
//....
directionsDisplay = new google.maps.DirectionsRenderer();
directionsDisplay.setMap(map);
directionsService = new google.maps.DirectionsService();
var request = {
origin : new google.maps.LatLng(origin.lat, origin.lng),
destination : new google.maps.LatLng(destination.lat, destination.lng),
travelMode : google.maps.DirectionsTravelMode.DRIVING,
unitSystem : google.maps.DirectionsUnitSystem.METRIC,
region: 'de'
};
directionsService.route(request, function(result, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(result);
}
});
//....
As a result I get something like this
Head southwest on 吳江路/吴江路 toward 泰兴路/泰興路
Turn left at 茂名北路
Continue onto 茂名南路
Turn right at 淮海中路
Slight left to stay on 淮海中路
Turn left at 华山路/華山路
The instructions are English on my browser, and French on my French colleagues French Firefox, the street names are Chinese, I thought I requested information in German region: 'de'
Now ok, maybe the Chinese streets are not available in German, but setting region to gb, en, even zh seems to do nothing.
I really would like the text just to be one language, preferably English.
edit I am quite sure the street names are available in English, because when I use the Geocoder results are in English e.g Shimen Road (No.1)
edit2 with http://maps.google.com/maps/api/js?sensor=false&language=cs I am able to force the instructions into a language, but still the street names are stuck in Chinese. Using the geocoder api i can receive chinese street names that are Chinese translated into English/German/French (with fallback to english when german/french translations are missing) so why the directions street names is stuck on Chinese does not make sense. It could be just a flaw/deliberate on google's side, but I kind of doubt it.
Is there a reason
DirectionRequest doesn't have a parameter to specify language. The language is distinguished according to the language used for the map. The language is either specified as an optional language parameter in the <script> tag e.g.
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false&language=cs">
or if the parameter is not present the browser's preferred language is used.
If you want to use different language for the direction results and the map, you can use Google Directions API:
http://code.google.com/apis/maps/documentation/directions/
The result is JSON text. To use it easily, it should be sufficient just to convert it to an object.
The region parameter (both in the maps' DirectionRequest and Directions API) doesn't change the language, it serves other purpose. It affects results to be biased towards some region (e.g. the default result for 'Toledo' is the city in Ohio USA, if you want the one in Spain, use region=es to bias the results).
I'm trying to use EWS to search a Task folder on Exchange 2010. I'm trying to limit the due dates of the Tasks returned, but unfortunately there's no equivalent to the CalendarView for the Task folder, so I have to use a FindItem search.
I'm using Java, Axis2 and I prepare the query like this:
// fiType is, obviously, a FindItemType
RestrictionType rType = fiType.addNewRestriction();
IsGreaterThanOrEqualToType igtoretType = IsGreaterThanOrEqualToType.Factory.newInstance();
igtoretType.addNewFieldURI().setFieldURI(UnindexedFieldURIType.TASK_DUE_DATE);
igtoretType.addNewFieldURIOrConstant().addNewConstant().setValue(dateFormat.format(begCal.getTime()));
IsLessThanOrEqualToType iltoretType = IsLessThanOrEqualToType.Factory.newInstance();
iltoretType.addNewFieldURI().setFieldURI(UnindexedFieldURIType.TASK_DUE_DATE);
iltoretType.addNewFieldURIOrConstant().addNewConstant().setValue(dateFormat.format(endCal.getTime()));
SearchExpressionType[] seArr = new SearchExpressionType[2];
seArr[0] = igtoretType;
seArr[1] = iltoretType;
AndType aType = rType.addNewAnd();
aType.setSearchExpressionArray(seArr);
Unfortunately, I get this error:
org.apache.axis2.AxisFault: La demande a échoué lors de la validation du schéma : L'élément 'http://schemas.microsoft.com/exchange/services/2006/types:SearchExpression' est abstrait ou son type l'est.
Roughly translated from French, it means that the query failed because the type SearchExpression is abstract, or it's type is.
After searching, I found this article explaining how to modify the types.xsd file to take care of this. However, even after applying the modifications, I still get the same error.
I'm at a loss as to how to go about solving this. Any help would be appreciated.
Another option is EWS Java API by Microsoft...