4
votes

In the assets folder of my Android App Project I am storing custom XML files which describe actions in my game. Since they are a little more complex, they can not be directly written as Android Resources.

Update: I am storing my custom (complex) XML files in the res/xml folder now.

Simple Example:

<dialog>
    <npc>1</npc>
    <text>Hello! How are you?</text>
</dialog>

What I want is to use the convenient advantages of Android String resources for language localisation. In the example above, I want to save Hello! How are you? in my res/values/strings.xml and then reference this somehow like this:

<text>@string/dialog_1_text</text>

I do not want to create different language res/xml folders where I copy all xml files and translate them completely. I only want parts of it to be translated. Easily by referencing a String resource.

Update 2: I have now found out that the XMLParser that you get from Resource.getXml() has a method called getAttributeResourceValue() which converts an attribute like randomAtt="@string/random_string" automatically to an actual Resource ID.
However, in the XML file, there still is no clear dependency and there is no preview for the string or a warning when you put in an invalid resource. But - since there even is a method for that - I strongly believe that it is possible to let the validator only allow string resources in some custom attributes.

1
Why do you want to store it as an asset if it has localizable strings? It will be better to store them in xml directory under resources rather than assets. - greenrobo
Because all XML files in the Resource folder need to follow a certain Android structure. And for some tasks I need more complex XML files with my own schemas. That's not allowed in res. - Tobias Baumeister
As per Android documentation, you can create a directory "xml" under resources and store arbitrary xml there. Have you explored that option? Did you run into any limitations in using this approach? - greenrobo
Ah, wow, how did I not notice that! Thank you! ... Though, the question remains: How do I now reference a string from inside that? I tried typing @string/ in a custom text-tag but there was no auto-complete, so I assume it's not referenceable. - Tobias Baumeister
I updated the first post, thanks to @greenrobo. Though, I still have no clue how to reference string resources in custom xml resource files now. - Tobias Baumeister

1 Answers

10
votes

Okay, so after a lot of research in the past two hours, I finally found a very well working solution. I have seen a few threads which ask very similar questions - that's why I am sure this answer might help some programmers in the future.

Requirements:
The requirements for my task were the following: Referencing String Resources in a custom XML file with my own schema, stored in the res/xml directory. The XML Validator of Android Studio should automatically detect it as a String resource, print a warning when it is invalid - and preview the actual String when it is a valid resource. Also it should be as performant as possible.

Solution:
And this is how I solved it:
Instead of putting the string resource between the tags, I had to put them as an attribute. No big deal in my case.
And for the validator to recognize them as String resource, I had to call these attributes text from the android namespace: Update: I found out that you can call the attribute whatever you like, and it is not necessary to include the android namespace.

<main>
    <nested>
        <test myText="@string/lorem_ipsum_100"/>
    </nested>
</main>

(Your custom XML may look as whatever you like/need! This is just an example - it works with unlimited nested tags and your own defined schema.)

I didn't think this was gonna work - but it actually does quite well! Whenever I hit Build project, it prints out a warning when I used an invalid String resource.
And not only that, it also previews it as desired: String preview in Android Studio

(Note: This screenshot was made before I noticed that you can call the attribute whatever you want/need. There is no need to call it explicitely android:text. Android Studio will automatically recognize it as a string resource, as long as you put @string/....)

Now last but not least, to let your Java code interpret the resource correctly, you have to do this:

XmlResourceParser parser = getResources().getXml(R.xml.tutorial_welcome_dialog);
try {
    while (parser.next() != XmlPullParser.END_DOCUMENT) {
        if (parser.getEventType() == XmlPullParser.START_TAG && parser.getName().equals("test")) {
            String s;
            for (int i = 0; i < parser.getAttributeCount(); i++) {
                if (parser.getAttributeName(i).equals("text"))
                    s = getResources().getString(parser.getAttributeResourceValue(i, -1));
            }
        }

    }
} catch (Exception e) {
    e.printStackTrace();
}

Performance note: As far as I can see, this solution is also super performant, since android pre-parses the @string/... automatically into Resource IDs.