Showing posts with label Globalization. Show all posts
Showing posts with label Globalization. Show all posts

Friday, March 13, 2009

How to change the language of SQL Developer's user interface?

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

Yesterday i upgraded SQL Developer from version 1.5.1 to 1.5.4 and when i opened it, i noticed that the user interface was automagically showing up in Italian.
I say automagically because i am italian indeed, but the operating system on this particular machine is localized in mexican-spanish, so i wonder if SQL Developer was spying my conversations... ;-)

While i appreciate the globalization efforts, i feel much better having certain programs with an English user interface for the simple reason that when i need to communicate with someone else who doesn't speak Italian, i can talk about menu entries, settings, button names using a commonly understood language and without having to guess what the original entry in English language could be.

How to get rid of this automatic translation then?
I started wandering through the preference menu and either because i am getting old or there are too many settings or this setting can't be changed in the preferences, i failed to find a list available translations.

After a quick OTN search i found a thread where users were reporting some problems when creating new connections and i noticed a workaround where one must add the following two lines to the Sqldeveloper.conf file in bin subdirectory.

AddVMOption -Duser.language=en
AddVMOption -Duser.country=US

I promptly tried it out to see if that could solve my problem and bingo!

I don't know if this is the only "supported" way of changing the user interface language, but it's certainly working.

Please let me know if there are any other options.


See more articles about SQL Developer.

PS: I encourage everyone interested in this feature to cast their vote in the SQLDeveloper Feature request application: https://apex.oracle.com/pls/apex/f?p=43135:7:0::NO:RP,7:P7_ID:10821

Friday, March 06, 2009

SAQ: Seldom Asked Questions about Apex Globalization

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

I like jokes as you can understand from the title of this posting, however i was in the mood for it because sometimes i happen to read FAQ lists where there are questions that i'd never ask in centuries, hence the idea of a SAQ list.

Apex globalization mechanism is extremely powerful and flexible, yet you need to know what you are doing if you don't like last minute surprises. In other words you need to master the globalization process if you don't want to accidentally delete translated text or put in jeopardy the whole application with a wrong action.

Before delving into the list of questions, let's take a tour of the main translation page of Oracle Application Express.

The Apex globalization process is managed from the page below, that you can easily access from the shared components page of the application builder:

The central region of this page outlines the steps from the beginning to the end of the globalization process and provides the links to the key phases of the process.

Step 1 in this list is a one-off task that needs to be performed only when:
  1. you need to create a new translated application
  2. you need to change an existing mapping or delete it altogether.
Step 2 is the real starting point each time you need to update an existing application when you've changed something that needs to be propagated to the various translations.

Step 3 is a curious fake link. Indeed it's just a reminder of what you need to do next, that is either translate the text yourself or ask someone else to do it for you.

Step 4 takes you to the page where you can see the list of XLIFF files loaded in the repository and you can apply one of them or you upload a freshly translated one.

Step 5 points to the page where you manage the static message translations and is optional. If you don't use translatable text messages, those retrieved via the API call to APEX_LANG.MESSAGE you don't need to go there. Translatable messages are extremely useful for translating static text that needs to be stored inside page items or application items or when the value needs to be returned through a PL/SQL function in the current language. See the linked page for a practical example.

Step 6 leads you to an even more sophisticated feature, the dynamic translation of text returned by dynamic LOVs, that is list of values based on queries. While text descriptions coming from static LOVs are automatically retrieved by Apex and inserted into the repository, this is not possible with dynamic LOVs that are based on user defined queries on arbitrary tables, however Apex gives you the possibility of performing dynamic translations, so, if you have a query like:
SELECT fruit_name d, fruit_id r
FROM fruits
you can get "apple" when viewing the page in English or "Apfel" when viewing it in German.


The menu called Navigate on the right hand side is a fine grained list of tasks and gives you instant access to the critical features.

The menu below it, called Translation Utilities, is also important because it's the only place where you find a link to the page for manually translating the text.

For some reason the link to the page that you'll visit most frequently has been named Export Translations, followed by the link Export XLIFF, which is the real page where you actually download a dump of the translation repository.

Why do i need to map an application?
Apex globalization mechanism works by creating a sort of ghost application with its own application ID.
Mapping means assigning a number to a translated application. In an hosted environment like apex.oracle.com, where there are thousands of application IDs already taken by other users, it can be tricky to find out a free application ID because Apex does not automatically suggest the next available number.
Note that Apex doesn't actually reserve the application ID until you publish the translation, so it might happen that you choose an ID but then it's also taken by another user before you had the time to publish the translation.
If that happens, you'll get a run-time error when you attempt to publish (but not at time of seeding):
ORA-20001: Error during execution of wwv_flow_copy: WWV_FLOWS
ORA-00001: unique constraint (FLOWS_030100.WWV_FLOW_FLOW_PK) violated
Each application ID maps to a language code, like it, fr, de, zh-cn, ja and so on.
Apex maintains a consistent session state whenever you switch from one language to another by any means, within the same apex session, which is a good thing.


1. What does it mean to seed an application?
Seeding is the process of preparing a translated version of your primary application.
When you seed the text, Apex inserts/updates/deletes the translation repository with the most recent version of the translatable strings. Seeding alone does not change anything in the currently running translated applications until you publish the translated version.
Note also that when you seed an application you are doing a twofold action: you are not only updating the source text but also the target text. An important thing to bear in mind is that the target text won't be touched only if the old source text and the new source text are equal, (which means that a single space makes a difference!), otherwise it will be "reset". See later on for a typical scenario.


2. What happens when i publish an application?
Publishing is the final step in the globalization process.
The application is created by assembling the metadata from the primary application and merging it with the translated text. Note that you cannot access directly a translated application by invoking Apex with the mapped application ID, if you attempt to do so, you'll get a page like this:



3. What if i need to update only a few new translated strings?
whenever you add a new element which is a candidate for a translation, like a page item or a new shared component like a list entry, translated applications won't pick them up until you seed the application to the desired language. After seeding the application, the fastest way to get a string translated is to manually edit it. After editing the translation, you can go straight to the publishing phase, without importing anything.
  1. seed the application
  2. click on translation home
  3. click on manually edit translations
  4. update the translations
  5. export the XLIFF file for future reference ( this step is not required but highly recommended!)
  6. publish the selected translation
4. What if i need to update a few existing strings because i changed the corresponding source element?
you must be careful when changing the text of a source element because Apex will automatically wipe out the translation upon seeding! Imagine you have a large text in the source language and you realize there is a grammatical error for instance. This grammatical error is probably absent in the target language(s) so you could easily have a situation where you want to change just the source element, not the existing translations.
So, you fix the source text, seed the application and bang, the old translation is lost!
The target element is erased and replaced with the new untranslated text.
This example demonstrates why you ought to export a full XLIFF file whenever you translate something, the file will be the backup in case something goes wrong.
Back to our problem, if you change a translatable string of an existing element, if you seed the application again, then you need to recover the translation from the XLIFF file, if it still applies, or provide a new one if it does not apply or if you didn't export the XLIFF file. See entry #3 on how to manually edit a translation.

5. What is an XLIFF?
XLIFF is an international file format standard based on XML tailored for translation tools. You can find out more on XLIFF on wikipedia.

6. Do i necessarily need to translate the content of the XLIFF file to translate an application?
No, you don't. You can manually edit the translations after seeding the translation. However, after doing so, you should immediately take an export in XLIFF format as a backup of your work!
The XLIFF format comes in handy if you are going to use some tool to translate the text, otherwise you can easily break the XML file format by deleting or inserting unwanted characters, especially if the translated text contains some HTML tags. There is a free on-line tool kindly provided by iAdvise, that allows you to manipulate XLIFF files generated by Apex.

7. Is there any apex dictionary view containing the translations?
At time of writing (version 3.1.2) there isn't any built-in apex dictionary view available and unless i overlooked it, there isn't any view in the just released version 3.2.

8. Why when i query certain apex dictionary views like apex_application_pages columns like help_text or label are always returned in the primary language?
The apex dictionary view doesn't currently support a language "context", so it always returns the text stored in the primary application, even when the query is originated from a translated application.
Hopefully one day apex dictionary views will fully support translated applications.

9. How can i compare two XLIFF files?
There are some tools around to do this specifically on XML files, besides some powerful plain text editors or Unix and DOS commands.
Some years ago i bought a license of XMLSpy, a powerful Windows based XML editor that comes with a document comparison function. I used it just yesterday to understand why i got two different links in two different application translations. As far as i know you can install a trial of XMLSpy valid for 30 days, so if it is a one-off requirement, it won't cost you a penny. Altova released also a stand-alone utility called diffDog for comparing files, so you might want to check it out as well.
As i am working more and more time on the Mac, today i searched for a file compare utility for Mac OS X and i found what it seems to me like a perfect match, a multiplatform utility called DeltaWalker.
I quickly installed the trial and it worked like a charm although i didn't perform any "stress test". Even in this case you have days ahead to evaluate the product before buying a license.
This was not meant to be a comprehensive software review, so you may want to conduct a deeper search on the web.

10. The Application Language Derived From attribute (in the Globalization Attributes page) contains several options, two of them are called Application Preference and Item Preference: what's the difference between the two?
The former option automatically retrieves the language from a user preference, supposing you have some process that calls the API procedure APEX_UTIL.SET_PREFERENCE. The preference name is called FSP_LANGUAGE_PREFERENCE, a name that may easily lead to some confusion with the other option.
This approach makes sense when the application requires authentication because it stores the value in a sort of user profile. The advantage is in that a returning user doesn't need to switch the language after the login, it will be automatically set by Apex (but he/she can still change it at any time afterwards).
The latter option works by storing the language option inside an application item called FSP_LANGUAGE_PREFERENCE (that you need to create yourself, don't forget it), so the user might have to change it every time if the primary language is not the preferred one. The advantage of this approach is in that it works also with public pages, where you can pick a specific language by clicking on an icon or selecting from a list. The logic for updating the application item must be written by the developer.

11. Is there any way to set a given language from the URL?
This problem has haunted me for quite some time.
The solution i found works well with real users but doesn't work well with web spiders like googlebot who is very picky with web redirects.
First of all you need to set the globalization attribute "Application Language Derived From" to "Item Preference (use item containing preference)".
Secondly you must set up an application process that runs before header performing a conditional redirect using the following PL/SQL call:
begin
:FSP_LANGUAGE_PREFERENCE := :REQUEST;
htp.init;
owa_util.redirect_url('f?p='||:APP_ID||':'||:APP_PAGE_ID||':'||:APP_SESSION);
end;
and the condition is "Request is contained within Expression1", where expression1 contains the list of the expected language codes like "en,de,fr,..." as shown in this sample page:

http://apex.oracle.com/pls/otn/f?p=multilangdemo:1:0:en
http://apex.oracle.com/pls/otn/f?p=multilangdemo:1:0:es

As i stated initially, redirecting the page may adversely affect the page indexing process, for instance Google spiders don't like at all redirects and this may prevent the link from being harvested. This is not a problem if the application is not aimed to the public and you don't expect users to reach you through a web search.

Note that setting the item FSP_LANGUAGE_PREFERENCE directly from the browser achieves curious results owing to the sequence of operations that Apex does when building a page. If you set this application item from an URL, apex will start building the page using the current language, then, at a certain point it will set the new value for the variable in the session state. This may result in pages with mixed languages, where static text is still in the old language and dynamic content is in the new language. If you reload the page though, the new language will be used. This is clearly a suboptimal solution.
There must be a reason if the Apex team decided to retrieve the current page language before setting values in session state, if not, may be one day we will be able to change the current language before the page loads without the need for an additional redirect.
Updated on march 13:
i simplified the argument in the call to OWA_UTIL.REDIRECT_URL, instead of using an absolute path, now i'm using a relative path, which makes the transition from a development or test environment to a production environment much easier because there is no need to worry about the current service path.

12. How to show the current page in a different language by pressing a button?
With a logic similar to that i explained in question #11, but replacing the redirect process with an application computation that updates the value of FSP_LANGUAGE_PREFERENCE after submitting the page, you can switch language in every page of your application by clicking on a button defined as illustrated below:
The second component is a simple conditional after submit application computation (a computation that will automatically run on each and every page if a condition is met) defined as follows:


With just a few components like a region of buttons (or icons or whatever you prefer) defined on page zero and a single application computation, you can easily enable language switching on each page. If you prefer you can move this function to the navigation bar, where you can define each entry as an URL containing a javascript call like "javascript:doSubmit('es');".
See how it works in a live demo application.

More infrequently asked questions to be added in the future.


See more articles about Oracle Application Express or download tools and utilities.

Wednesday, January 30, 2008

Extending supported languages for "since" date formats in Apex

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

Although Apex (3.0.1) comes with 9 optional languages (German, Spanish, French, Italian, Japanese, Korean, Brazilian Portuguese, Simplified Chinese, and Traditional Chinese) that you can install to localize Apex on-line help plus some locale information like the "SINCE" date format mask, the developer can freely create applications for languages not comprised in this set (see the list of all supported language codes).

The package APEX_UTIL contains several useful procedures and functions and one of these packaged functions is:
APEX_UTIL.GET_SINCE(date);
This function returns a string representing in words the elapsed time since a certain date (<= SYSDATE) specified as a parameter. A typical usage is for indicating how old a certain entry is, for instance news, blog postings, forum messages, emails and so on. Note that Apex's SINCE date format mask embeds the function at the report attributes level, so you don't need to explicitly include it in the query source normally. Although APEX_UTIL.GET_SINCE is a globalized function, translated versions of the text require to install apex in target language(s), so it might come in handy the following apocryphal version that makes use of Apex's globalization techniques, that is by means of translatable text messages. This can be especially useful if you need to translate the strings in one of the languages that are not directly supported by Apex.
Bear in mind however that a separate copy of each translated message will be required for each installed application.

Download get_since.sql source file.

Another difference is in that i adopted a somewhat relaxed concept of time to describe how old is a certain date, but you can change the function to suit your needs by increasing, reducing or modifying the time ranges.

See an example of Apex's standard function compared with the custom version.

Besides English, I've loaded Spanish and Italian translations in the sample application, but you must have the browser language set accordingly in order to see them.
All other languages will display messages in English.

You can use the linked script to initialize the English text messages for a certain application or modify it for entering additional translations, just change the language code and the text.

These messages can be edited individually from the Text Messages page in the globalization section of the shared components within Apex.
Note also that you don't need to create a translated version of the application for displaying the text in the desired language.

Finally, a quick tip for dealing with a report query that includes this custom function, as the most typical situation is a news report sorted by descending publication date (see the picture below):


  1. the query must contain both the column containing the date value as well as the result of get_since (in the picture columns PUBDATE and AGE respectively).
  2. Check the sort option for the date column and leave unchecked the sort option for get_since result (as pictured above). This setting is necessary to sort items properly because get_since returns a string that would result in a meaningless alphabetical sort.
  3. You can sort the report basing on the real date column without displaying it in the report. In this fashion you'll get a report displaying just how old are the entries, starting from the most recent to the oldest.
  4. Remember that you can combine column values as you prefer, you can even group different columns inside the HTML Expression property of the report column attribute page (see picture below), the current column value can be manipulated using the #COLUMN_NAME# syntax (see #AGE# in the example below), in this way you can create reports formatted exactly as you want.

Tuesday, March 27, 2007

Oracle APEX Globalization Tips for sleeping well

Yesterday it was my G-day: alea iacta est!

I finally decided to go global with a tiny application i am developing and i embarked myself on the translation mission.

I was really eager to start investigating the built-in translation facilities of Oracle Application Express as i felt that:
  1. There was a huge gap in the knowledge of this area on my side.
  2. I had the impression that this was one of the strongest points of Apex: a robust, flexible and powerful mechanism for delivering a localized application in almost every aspect.

Funnily enough, both facts proved to be true, at my expense.

I set out and took this application in its current development status and, following the recipe in the on-line help, i created a mapped english version.
Then i started playing aroung with XLIFF files, exporting, importing, checking and unchecking options in order to understand how the process works.
I just didn't realize exactly what Oracle meant by seeding an application.
Or better said, i didn't understand early enough the implications of seeding.

I thought it was just a way of freezing the value of labels, titles and so on, but actually it was much more than that.
One must understand that, if a dedicated application ID is required to handle each translated version, that means a lot more than just translated labels.

So, after a while i left this globalization exercise and went back to develop my application.
Strange things began to happen.
I must say, begging for pity, that yesterday i was tired, my back was in pain, the night before i went to sleep very late and woke up very early, there was a flood, the grasshoppers...
IT WASN'T MY FAULT!

Anyway, i was modifying some processes in a page and when i went out to try them, either they didn't work as expected or they did something weird. Values disappearing without explanation. Processes that didn't fire. Checkpoints ignored.

I was really upset.

The fact is i had completely overlooked my previous globalization experiments.

Finally, for some reason, i took the decision that saved my night or what remained of it.

I changed the request string of a button. I don't even know why i did that, i was almost desperate, i had rewritten code, attempting to raise errors that got never fired, i was to the point to give up!

Then i went to test this final change, sort of Armageddon, to see what the hell was happening with my program, and to my big astonishment, the button was still carrying the old request.

There was a light at the end of this tunnel.

Eventually i realized that i was testing the page with a "wrong" language setting, diverting me to the translated, globalized, yet dramatically old version of the code.

As i translated only the first page and the others were still carrying the original untranslated text, i could not spot the difference between the new and the old one, moreover i hadn't change any items or layout thing, i had just been working on processes, so it looked the same, but it wasn't doing the same!

I changed the browser's language setting to match the application's primary language and everything was starting to work normally again. EUREKA!

Moral, here are my commandments:
  1. Go to bed on time.
  2. Translate an application when it's finished, not when you are still muddling the whole thing.
  3. Make translated labels look different. Do something, translate them or just put a reminder, insert something that let's you understand at once that what you see is not the original primary application's text.
  4. Set the primary language in the browser such that it matches the primary language of the application, so you don't get diverted to a translated version unless you explicitly want to test that particular version.
In case you prefer to breach my commandments, remember to seed the application for each translated version you've mapped to your primary application's language every time you make substantial changes.
Seeding will keep all application versions in sync, recycling translated attributes, purging what's no longer there and adding new entries.

But in the end i am really amazed by how simple and quick is to create a translated version of an application, even if i was almost to the point of throwing my laptop out of the window.


Happy globalization!

yes you can!

Two great ways to help us out with a minimal effort. Click on the Google Plus +1 button above or...
We appreciate your support!

latest articles