Skip to content

Commit

Permalink
MONDRIAN: Fix MONDRIAN-367, "i18n support in Mondrian XMLA". XMLA ser…
Browse files Browse the repository at this point in the history
…ver now

    observes the 'locale' parameter, and if present, passes it
    to the underlying olap4j connection. Locale can be a LCID code per the XMLA
    spec (e.g. 1033) but can also be a locale name (e.g. 'en_US').

    Add a mechanism to provide multiple translations of element captions &
    descriptions in a schema file. The mechanism uses annotations named
    '<prop>.<locale>' where '<prop>' is either 'caption' or 'description'.
    This mechanism is experimental and may be removed without notice in future
    versions of mondrian. (I have in mind a better way to create multi-locale
    schema files, without necessarily embedding all of these language strings
    in the schema file.)

    But anyway. The mechanism allows us to write a unit test that proves that
    the locale is getting through the XMLA layer.

    The mechanism applies to cube, dimension, hierarchy, level. I don't think
    it's possible to localize measures, members, named sets, properties just
    yet.

    Probably still need to change the olap4j-driver-for-xmla to pass the client
    olap4j connection's locale in XMLA requests. And this ain't gonna work at
    all if you use the legacy mondrian.olap API.

[git-p4: depot-paths = "//open/mondrian/": change = 14504]
  • Loading branch information
julianhyde committed Jul 30, 2011
1 parent 1e11405 commit 18d6c3d
Show file tree
Hide file tree
Showing 23 changed files with 326 additions and 78 deletions.
12 changes: 11 additions & 1 deletion demo/FoodMart.xml
Expand Up @@ -6,7 +6,7 @@
== Agreement, available at the following URL:
== http://www.eclipse.org/legal/epl-v10.html.
== Copyright (C) 2000-2002 Kana Software, Inc.
== Copyright (C) 2002-2009 Julian Hyde and others
== Copyright (C) 2002-2011 Julian Hyde and others
== All Rights Reserved.
== You must accept the terms of that agreement to use this software.
-->
Expand Down Expand Up @@ -115,6 +115,16 @@ WHERE "product"."product_class_id" = "product_class"."product_class_id"

<!-- Sales -->
<Cube name="Sales" defaultMeasure="Unit Sales">
<!-- Use annotations to provide translations of this cube's caption and
description into German and French. Use of annotations in this manner is
experimental and unsupported; just for testing right now. -->
<Annotations>
<Annotation name="caption.de_DE">Verkaufen</Annotation>
<Annotation name="caption.fr_FR">Ventes</Annotation>
<Annotation name="description.fr_FR">Cube des ventes</Annotation>
<Annotation name="description.de">Cube Verkaufen</Annotation>
<Annotation name="description.de_AT">Cube den Verkaufen</Annotation>
</Annotations>
<Table name="sales_fact_1997">
<!--
<AggExclude name="agg_l_03_sales_fact_1997" />
Expand Down
20 changes: 17 additions & 3 deletions src/main/mondrian/olap/OlapElement.java
Expand Up @@ -4,16 +4,15 @@
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// Copyright (C) 1998-2002 Kana Software, Inc.
// Copyright (C) 2001-2009 Julian Hyde and others
// Copyright (C) 2001-2011 Julian Hyde and others
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
//
// jhyde, 21 January, 1999
*/

package mondrian.olap;

import org.olap4j.metadata.Member;
import java.util.Locale;

/**
* An <code>OlapElement</code> is a catalog object (dimension, hierarchy,
Expand All @@ -40,6 +39,16 @@ OlapElement lookupChild(
String getQualifiedName();

String getCaption();

/**
* Returns the value of a property (caption or description) of
* this element in the given locale.
*
* @param locale Locale
* @return Localized caption or description
*/
String getLocalized(LocalizedProperty prop, Locale locale);

Hierarchy getHierarchy();

/**
Expand Down Expand Up @@ -79,6 +88,11 @@ OlapElement lookupChild(
* @return Whether this element is visible
*/
boolean isVisible();

enum LocalizedProperty {
CAPTION,
DESCRIPTION
}
}

// End OlapElement.java
48 changes: 43 additions & 5 deletions src/main/mondrian/olap/OlapElementBase.java
Expand Up @@ -4,17 +4,17 @@
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// Copyright (C) 2001-2002 Kana Software, Inc.
// Copyright (C) 2001-2007 Julian Hyde and others
// Copyright (C) 2001-2011 Julian Hyde and others
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
//
// jhyde, 6 August, 2001
*/

package mondrian.olap;

import org.apache.log4j.Logger;

import java.util.Locale;
import java.util.Map;

/**
* <code>OlapElementBase</code> is an abstract base class for implementations of
* {@link OlapElement}.
Expand All @@ -26,7 +26,6 @@
public abstract class OlapElementBase
implements OlapElement
{

protected String caption = null;

protected boolean visible = true;
Expand Down Expand Up @@ -97,6 +96,45 @@ public void setCaption(String caption) {
public boolean isVisible() {
return visible;
}

public String getLocalized(LocalizedProperty prop, Locale locale) {
if (this instanceof Annotated) {
Annotated annotated = (Annotated) this;
final Map<String, Annotation> annotationMap =
annotated.getAnnotationMap();
if (!annotationMap.isEmpty()) {
String seek = prop.name().toLowerCase() + "." + locale;
for (;;) {
for (Map.Entry<String, Annotation> entry
: annotationMap.entrySet())
{
if (entry.getKey().startsWith(seek)) {
return entry.getValue().getValue().toString();
}
}

// No match for locale. Is there a match for the parent
// locale? For example, we've just looked for
// 'caption.en_US', now look for 'caption.en'.
final int underscore = seek.lastIndexOf('_');
if (underscore < 0) {
break;
}
seek = seek.substring(0, underscore - 1);
}
}
}

// No annotation. Fall back to the default caption/description.
switch (prop) {
case CAPTION:
return getCaption();
case DESCRIPTION:
return getDescription();
default:
throw Util.unexpected(prop);
}
}
}

// End OlapElementBase.java
4 changes: 4 additions & 0 deletions src/main/mondrian/olap/Query.java
Expand Up @@ -1917,6 +1917,10 @@ public Hierarchy getHierarchy() {
public Dimension getDimension() {
throw new UnsupportedOperationException();
}

public String getLocalized(LocalizedProperty prop, Locale locale) {
throw new UnsupportedOperationException();
}
}

/**
Expand Down
6 changes: 4 additions & 2 deletions src/main/mondrian/olap/fun/FunUtil.java
Expand Up @@ -7,8 +7,6 @@
// Copyright (C) 2002-2011 Julian Hyde and others
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
//
// jhyde, 3 March, 2002
*/
package mondrian.olap.fun;

Expand Down Expand Up @@ -3265,6 +3263,10 @@ public String getCaption() {
throw new UnsupportedOperationException();
}

public String getLocalized(LocalizedProperty prop, Locale locale) {
throw new UnsupportedOperationException();
}

public boolean isVisible() {
throw new UnsupportedOperationException();
}
Expand Down
10 changes: 2 additions & 8 deletions src/main/mondrian/olap4j/MondrianOlap4jConnection.java
Expand Up @@ -633,17 +633,11 @@ SelectNode toOlap4j(Query query) {
}

public void setLocale(Locale locale) {
if (locale == null) {
throw new IllegalArgumentException("locale must not be null");
}
this.locale = locale;
mondrianConnection.setLocale(locale);
}

public Locale getLocale() {
if (locale == null) {
return Locale.getDefault();
}
return locale;
return mondrianConnection.getLocale();
}

public void setRoleName(String roleName) throws OlapException {
Expand Down
11 changes: 7 additions & 4 deletions src/main/mondrian/olap4j/MondrianOlap4jCube.java
Expand Up @@ -10,6 +10,7 @@
package mondrian.olap4j;

import mondrian.olap.Id;
import mondrian.olap.OlapElement;
import mondrian.olap.Role;
import mondrian.olap.SchemaReader;
import mondrian.olap.Util;
Expand Down Expand Up @@ -122,13 +123,15 @@ public String getUniqueName() {
}

public String getCaption() {
// todo: i81n
return cube.getCaption();
return cube.getLocalized(
OlapElement.LocalizedProperty.CAPTION,
olap4jSchema.getLocale());
}

public String getDescription() {
// todo: i81n
return cube.getDescription();
return cube.getLocalized(
OlapElement.LocalizedProperty.DESCRIPTION,
olap4jSchema.getLocale());
}

public boolean isVisible() {
Expand Down
14 changes: 9 additions & 5 deletions src/main/mondrian/olap4j/MondrianOlap4jDimension.java
Expand Up @@ -3,14 +3,16 @@
// This software is subject to the terms of the Eclipse Public License v1.0
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// Copyright (C) 2007-2010 Julian Hyde
// Copyright (C) 2007-2011 Julian Hyde
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
*/
package mondrian.olap4j;

import mondrian.olap.DimensionType;
import mondrian.olap.OlapElement;
import mondrian.olap.Util;

import org.olap4j.OlapException;
import org.olap4j.impl.*;
import org.olap4j.metadata.*;
Expand Down Expand Up @@ -87,13 +89,15 @@ public String getUniqueName() {
}

public String getCaption() {
// TODO: localize caption
return dimension.getCaption();
return dimension.getLocalized(
OlapElement.LocalizedProperty.CAPTION,
olap4jSchema.getLocale());
}

public String getDescription() {
// TODO: localize description
return dimension.getDescription();
return dimension.getLocalized(
OlapElement.LocalizedProperty.DESCRIPTION,
olap4jSchema.getLocale());
}

public boolean isVisible() {
Expand Down
27 changes: 12 additions & 15 deletions src/main/mondrian/olap4j/MondrianOlap4jHierarchy.java
Expand Up @@ -3,24 +3,19 @@
// This software is subject to the terms of the Eclipse Public License v1.0
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// Copyright (C) 2007-2010 Julian Hyde
// Copyright (C) 2007-2011 Julian Hyde
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
*/
package mondrian.olap4j;

import java.util.*;
import mondrian.olap.OlapElement;

import org.olap4j.OlapException;
import org.olap4j.impl.AbstractNamedList;
import org.olap4j.impl.Named;
import org.olap4j.impl.NamedListImpl;
import org.olap4j.impl.Olap4jUtil;
import org.olap4j.metadata.Dimension;
import org.olap4j.metadata.Hierarchy;
import org.olap4j.metadata.Level;
import org.olap4j.metadata.Member;
import org.olap4j.metadata.NamedList;
import org.olap4j.impl.*;
import org.olap4j.metadata.*;

import java.util.List;

/**
* Implementation of {@link org.olap4j.metadata.Hierarchy}
Expand Down Expand Up @@ -115,13 +110,15 @@ public String getUniqueName() {
}

public String getCaption() {
// todo: localize caption
return hierarchy.getCaption();
return hierarchy.getLocalized(
OlapElement.LocalizedProperty.CAPTION,
olap4jSchema.getLocale());
}

public String getDescription() {
// todo: localize description
return hierarchy.getDescription();
return hierarchy.getLocalized(
OlapElement.LocalizedProperty.DESCRIPTION,
olap4jSchema.getLocale());
}

public boolean isVisible() {
Expand Down
17 changes: 9 additions & 8 deletions src/main/mondrian/olap4j/MondrianOlap4jLevel.java
Expand Up @@ -9,20 +9,19 @@
*/
package mondrian.olap4j;

import mondrian.olap.OlapElement;
import mondrian.olap.Role;
import mondrian.olap.Util;
import mondrian.rolap.RolapConnection;
import mondrian.server.Execution;
import mondrian.server.Locus;
import mondrian.server.Statement;

import org.olap4j.OlapException;
import org.olap4j.metadata.*;
import org.olap4j.impl.ArrayNamedListImpl;
import org.olap4j.impl.Named;

import java.util.*;

import mondrian.olap.Util;

/**
* Implementation of {@link Level}
* for the Mondrian OLAP engine.
Expand Down Expand Up @@ -188,13 +187,15 @@ public String getUniqueName() {
}

public String getCaption() {
// todo: localized captions
return level.getCaption();
return level.getLocalized(
OlapElement.LocalizedProperty.CAPTION,
olap4jSchema.getLocale());
}

public String getDescription() {
// todo: localize
return level.getDescription();
return level.getLocalized(
OlapElement.LocalizedProperty.DESCRIPTION,
olap4jSchema.getLocale());
}

public int getCardinality() {
Expand Down
12 changes: 8 additions & 4 deletions src/main/mondrian/olap4j/MondrianOlap4jNamedSet.java
Expand Up @@ -8,6 +8,8 @@
*/
package mondrian.olap4j;

import mondrian.olap.OlapElement;

import org.olap4j.metadata.NamedSet;
import org.olap4j.metadata.Cube;
import org.olap4j.mdx.ParseTreeNode;
Expand Down Expand Up @@ -53,13 +55,15 @@ public String getUniqueName() {
}

public String getCaption() {
// todo: i18n
return namedSet.getCaption();
return namedSet.getLocalized(
OlapElement.LocalizedProperty.CAPTION,
olap4jCube.olap4jSchema.getLocale());
}

public String getDescription() {
// todo: i18n
return namedSet.getDescription();
return namedSet.getLocalized(
OlapElement.LocalizedProperty.DESCRIPTION,
olap4jCube.olap4jSchema.getLocale());
}

public boolean isVisible() {
Expand Down

0 comments on commit 18d6c3d

Please sign in to comment.