Eine Oracle APEX Anwendung mehrsprachig erstellen Part 2
Wie in unserem vorherigen Blog erwähnt, möchten wir euch auch noch eine zweite Alternative vorstellen, die in einem Kundenprojekt durchgeführt wurde. Diesmal werden wir nicht den Ansatz von APEX verfolgen, sondern uns einen eigenständigen Übersetzungsprozess erarbeiten.
Wie machen wir das?
Anstelle auf das APEX Repository zuzugreifen, erstellen wir uns ein eigenes Repository in dem wir alle notwendigen Übersetzungen und verfügbaren Sprachen hinterlegen. Dieses kann bereits eine einfache Tabelle im beliebigen Datenbankschema sein.
Warum machen wir das?
Der große Vorteil an dieser Variante ist, dass wir von nun an mit beliebig vielen APEX Anwendungen und darüber hinaus auch Anwendungen aus anderer Herkunft (zum Beispiel ein BI-System, weitere Web-Anwendungen etc.) auf dieses Repository zugreifen können. Wird ein Text aktualisiert, aktualisieren sich alle dahinterliegenden Anwendungen automatisch ohne weiteren Aufwand. Kommt eine weitere Sprache hinzu, so ergänzt man diese zunächst in der Repository-Tabelle und stellt dann in den Anwendungen die neu hinzugefügte Sprache als Auswahlkriterium bereit.
Das klingt erstmal gut…ABER?
Wo viele Vorteile zu erkennen sind, sind meistens auch Nachteile versteckt. Verfolgen wir diesen Ansatz so werden wir schnell feststellen das der Programmieraufwand doch erheblich aufwendiger ist und wesentlich mehr Zeit beansprucht werden muss. Dies sollte euch und/oder dem Kunden zuvor bewusst sein. Zudem sollte man schauen das die Anwendungen nicht allzu groß werden, da sonst an manchen Stellen die Übersicht verloren gehen kann.
Kommen wir zum eigentlichen Prozess
Wie zuvor beschrieben benötigen wir als erstes ein Repository in unserer Datenbank wo wir zukünftig alle Übersetzungen ablegen. Dazu legen wir eine einfache Tabelle an.
CREATE TABLE TRANSLATIONS
(TST_ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY,
TST_LANGUAGE VARCHAR2(10),
TST_TEXT_ID NUMBER,
TST_TEXT VARCHAR2(100),
CONSTRAINT translations_pk PRIMARY KEY (tst_id),
CONSTRAINT translations_uk UNIQUE (TST_LANGUAGE, TST_TEXT_ID)
);
In dieser Tabelle können wir jetzt unsere Übersetzungen hinterlegen, die wir später in unserer Anwendung anzeigen werden. Die Kombination „TST_LANGUAGE“ und TST_TEXT_ID“ ist dabei unser eindeutiger Indikator. Für unser Beispiel fügen wir ein paar Dummywerte der Tabelle hinzu.
INSERT INTO TRANSLATIONS (TST_LANGUAGE, TST_TEXT_ID, TST_TEXT) VALUES ('en', 1, 'Employee Number');
INSERT INTO TRANSLATIONS (TST_LANGUAGE, TST_TEXT_ID, TST_TEXT) VALUES ('en', 2, 'Name');
INSERT INTO TRANSLATIONS (TST_LANGUAGE, TST_TEXT_ID, TST_TEXT) VALUES ('en', 3, 'Job');
INSERT INTO TRANSLATIONS (TST_LANGUAGE, TST_TEXT_ID, TST_TEXT) VALUES ('en', 4, 'Salary');
INSERT INTO TRANSLATIONS (TST_LANGUAGE, TST_TEXT_ID, TST_TEXT) VALUES ('de', 1, 'Personalnummer');
INSERT INTO TRANSLATIONS (TST_LANGUAGE, TST_TEXT_ID, TST_TEXT) VALUES ('de', 2, 'Name');
INSERT INTO TRANSLATIONS (TST_LANGUAGE, TST_TEXT_ID, TST_TEXT) VALUES ('de', 3, 'Job');
INSERT INTO TRANSLATIONS (TST_LANGUAGE, TST_TEXT_ID, TST_TEXT) VALUES ('de', 4, 'Gehalt');
Datenbankseitig sind nun alle Vorbereitungen getroffen, so dass wir nun zur eigentlichen APEX Anwendung kommen. In diesem Beispiel werden wir einen einfachen Report erstellen, der über das Sprachmenü in der jeweiligen Sprache ausgegeben werden kann. Hierzu erstellen wir eine neue Anwendung und fügen einen Classic Report hinzu. Als SQL-Query für den Report verwenden wir folgendes Statement.
SELECT EMPNO, ENAME, JOB, SAL FROM EMP;
Als nächstes müssen wir ein Application-Item unter den „Shared Components“ anlegen. Dieses nennen wir „APP_LANGUAGE“ und wählen den Security Level „Checksum Required – Session Level“. Zu dem Application-Item hinterlegen wir noch eine Application-Computation die wir ebenfalls unter den „Shared Componnents“ finden.
- Computation Item: APP_LANGUAGE
- Computation Type: Static Assignment
- Computation: en
Die Computation wird benötigt damit das Application-Item „APP_LANGUAGE“ zu Begin einen Wert hat und die Anwendung weiß in welcher Sprache Sie angezeigt werden soll. Hier könnte man auch anstelle einer statischen Angabe die Browsersprache oder eine in der Datenbank hinterlegte Sprache vom User hinterlegen. Aber für unser kleines Beispiel reicht uns dieser einfache Weg.
Um unsere Übersetzungen in der Anwendung ausgeben zu können müssen wir jetzt für jeden übersetzten Titel ein weiteres Application-Item anlegen. In unserer Demo wären das:
- APP_EMPLOYEE_NUMBER_LABEL
- APP_NAME_LABEL
- APP_JOB_LABEL
- APP_SALARY_LABEL
Diese werden wir jedoch nicht statisch über eine Computation füllen, sondern über einen Application-Process, der die Übersetzungen aus der Datenbank lädt und den entsprechenden Item´s zuordnet.
Hierzu legen wir einen neuen Application-Process an den wir „get_labels“ nennen und als Point „After Authentication“ einstellen. Als PL/SQL Code verwenden wir folgendes Statement.
SELECT MAX(CASE WHEN TST_TEXT_ID = 1 THEN TST_TEXT END) AS APP_EMPLOYEE_NUMBER_LABEL,
MAX(CASE WHEN TST_TEXT_ID = 2 THEN TST_TEXT END) AS APP_NAME_LABEL,
MAX(CASE WHEN TST_TEXT_ID = 3 THEN TST_TEXT END) AS APP_JOB_LABEL,
MAX(CASE WHEN TST_TEXT_ID = 4 THEN TST_TEXT END) AS APP_SALARY_LABEL
INTO :APP_EMPLOYEE_NUMBER_LABEL,
:APP_NAME_LABEL,
:APP_JOB_LABEL,
:APP_SALARY_LABEL
FROM TRANSLATIONS
WHERE TST_LANGUAGE = :APP_LANGUAGE;
Jetzt haben wir erreicht das nach erfolgreichem Login die benötigten Übersetzungen aus der Datenbank geladen wurden und den entsprechenden Items zugeordnet sind.
Starten wir jetzt die Anwendung und schauen in den Session States uns die Application-Items an, sehen wir folgendes Ergebnis.
Aktuell passiert jedoch sonst nichts Weiteres mit unseren Labels. Somit müssen wir diese als nächstes in unserer Anwendung positionieren. Dazu wechseln wir zu unserem Classic Report und gehen einzeln die Columns durch. Bei jeder Column muss das statisch hinterlegte „Heading“ durch einen Substitution-String ersetzt werden der dann auf unser Application–Item verweist. Substitution-Strings beginnen immer mit einem „&“ gefolgt vom Item-Name und endend mit einem „.“. Zum Beispiel schreiben wir für die Column „EMPNO“ im Heading „&APP_EMPLOYEE_NUMBER_LABEL.“, für „ENAME“ -> „&APP_NAME_LABEL.“ usw.
Jetzt haben wir die Übersetzungstexte erfolgreich aus der Datenbank geladen, Application-Items zugeordnet und in der Anwendung positioniert. Laden wir erneut die Seite mit dem Classic Report sehen wir jetzt das die Überschriften die Texte aus der Datenbank bzw. aus den Application-Items ausgeben.
Zuletzt müssen wir nun noch die Auswahl der Sprache integrieren, so dass der Anwender zwischen den Sprachen wählen kann. Dazu erstellen wir eine Auswahlliste mit den Sprachen in der Navigationsleiste. Gehe hierzu zu den „Shared Components“ > „Navigation“ > „Navigation Bar List“ > „Desktop Navigation Bar“. Zunächst benötigen wir den übergeordneten Eintrag „Language“. Hierzu über „Create Entry“ einen neuen Eintrag anlegen und bei „List Entry Label“ „Language“ eingeben. Alles weitere kann außer Acht gelassen werden. Nun legen wir für jede Sprache (in unserem Beispiel „Englisch“ und „Deutsch“) weitere untergeordnete Einträge an. Also wieder „Create Entry“ und nun folgendes eingeben.
- List Entry Label: English (German)
- Parent List Entry: Language
- Target type: page in this application
- Page: &APP_PAGE_ID.
- Request: LANG
- Set these items: APP_LANGUAGE
- With these values: en (de)
Damit nach Auswahl der Sprache die Übersetzungen neu geladen werden benötigen wir noch einen weiteren Application-Process der auf den zuvor hinterlegten Request „LANG“ reagiert.
Zurück zu den „Shared Components“ und unter Application-Processes einen neuen Process erstellen. Als Name verwenden wir jetzt „get_labels_change_language“ und als Process-Point wird „On load: Before Header (page template header)“ verwendet. Der PL/SQL Code ist der identische vom vorherigen Application-Process. Wichtig ist noch, dass wir den Condition Type „Request = Expression1“ auswählen und als Expression „LANG“ eingeben, so dass der Application-Process startet sobald der Request „LANG“ ausgeführt wird.
Geschafft!
Unsere Anwendung ist jetzt mehrsprachig und der Classic Report kann in unterschiedlichen Sprachen ausgegeben werden. Dieses Szenario kann natürlich noch auf weitere Texte ausgeübt werden (zum Beispiel Sign Out -> Abmelden oder Language -> Sprache). Einfach wieder Datenbankeinträge vornehmen, Application Items anlegen, den Application Process erweitern und die Substitution-Strings an gewählter Stelle hinterlegen.
Mein persönliches Fazit:
Wie man sieht gibt es immer verschiedene Möglichkeiten Aufgaben umzusetzen. Welcher Weg der Beste ist kann an dieser Stelle nicht pauschal gesagt werden. Persönlich würde ich die in Part 1 beschriebene Variante vorziehen, sicherlich gibt es jedoch auch Anwendungsfälle wo dieser beschriebene Weg sinnvoller ist. Auch wenn hier der Entwicklungsprozess im ersten Moment aufwendiger wirkt kann auf langer Sicht gesehen dieser Prozess deutlich mehr Geschwindigkeit mit sich bringen. Man könnte kurzerhand eine kleine Anwendung zur Verfügung stellen, um die Übersetzungen in einem schönen Web-Formular zu pflegen. So bräuchte der Anwender nichts mehr in der Datenbank vornehmen, oder wie in Part 1 beschrieben den Prozess mit den XLIF Dateien durchgehen. So kommt man ganz ohne Admin-Rechte zum gewünschten Ziel.
Sicherlich gibt es auch noch weitere Methoden, auf die ich mich freuen würde wenn sie uns jemand mitteilen würde.
Sicherlich gibt es auch noch weitere Methoden, auf die ich mich freuen würde wenn sie uns jemand mitteilen würde.