tomkowz / Swifternalization

Localize iOS apps in a smarter way using JSON files. Swift framework.
MIT License
579 stars 47 forks source link

New type of files for next Swifternalization to reduce boilerplate code #9

Closed tomkowz closed 9 years ago

tomkowz commented 9 years ago

I would like to decrease level of boilerplate code that people have to write. To do this I have to stop using .strings file and create own file types.

I also would like to support new iOS 9 feature for string length variations described on my blog recently: http://szulctomasz.com/ios-strings-with-length-variations/

I was playing with current Localizable.strings and Expressions.strings and tried to add support for string length variations and there was a lot of repeating identifiers everywhere. I sad "No, I don't want to do this even to my enemy! I want to keep it simple".

And there you go. I started to thinking about new file type that will be loaded by framework and let's say "compiled" to check whether file is valid (has valid structure) and expressions and key-value pairs will be loaded.

This is how "expressions" file look like for now. File contains group of expressions. Every group has name as language code, so: "pl", "en", "de", etc. The same names as Xcode is creating for .lproj directories.

"base" {
    "one": "ie:x=1"
    "two": "ie:x=2"
    "more": "exp:(^[^1])|(^\\d{2,})" 
}

"pl" {
    "few": "exp:(((?!1).[2-4]{1})$)|(^[2-4]$)"
}

And there is second file which contains key-value pairs for localization. Every file contains key-value pairs for specified language. You can e.g. have "base", "pl", "en", "de" files.

Here is example for "base":

/* 
Displayed at welcome screen
*/
"welcome": "welcome"

/* 
Displays string with number of cars
1 car or X cars
*/
"cars" {
    "one": "1 car"
    "ie:x=2": "2 cars"
    "more": "%d cars"
}

/*
Forgot password sentence displayed on login form
*/
"forgot-password" {
    @100: "Forgot Password? Help."
    @200: "Forgot Password? Get password Help."
    @300: "Forgotten Your Password? Get password Help."
}

/*
Sentence about cars
*/
"car-sentence" {
    "one" {
        @100: "one car"
        @200: "just one car"
        @300: "you've got just one car"
    }

    "more" {
        @100: "%d cars"
        @300: "you've got %d cars"
    }
}

The file can contain simple key-value pairs like "welcome" or extended one like "car-sentence".

The "car-sentence" uses shared expressions like "one" and "more". And you can see there is also support for string length variations (100, 200, 300) which stand for: up to 100 width, up to 200 width, up to 300 width or bigger.

I think this files looks nice and maintaining this will be easier than extended Localizable.strings. As you can see it also avoid using .stringsdict which I don't like a lot.

The first questions are:

I am thinking about loading it and parsing. I think I should implement some kind of compiler which will take a file and check whether it has correct structure/content and then will parse it and another part of framework will get expressions and key-value pairs from them.

The question from me is: How to correctly and in some easy way at least for beginning check correctness of the files? What are popular approach when doing this?

tomkowz commented 9 years ago

I spoke with colleague today about the problem and we agreeably think that doing own file type will be overkill and will complex the code a lot because of necessity to implement some kind of lexical analyzer/compiler. After playing a while with JSON I came out with following structure proposition.

expressions.json

{
    "base": {
        "one": "ie:x=1",
        "two": "ie:x=2",
        "more": "exp:(^[^1])|(^\\d{2,})"
    },

    "pl": {
        "few": "exp:(((?!1).[2-4]{1})$)|(^[2-4]$)"
    }
}

_somelanguage.json

{
    "welcome": "welcome",

    "cars": {
        "one": "1 car",
        "ie:x=2": "2 cars",
        "more": "%d cars"
    },

    "forgot-password": {
        "@100": "Forgot Password? Help.",
        "@200": "Forgot Password? Get password Help.",
        "@300": "Forgotten Your Password? Get password Help."
    },

    "car-sentence": {
        "one": {
            "@100": "one car",
            "@200": "just one car",
            "@300": "you've got just one car"
        },

        "more": {
            "@100": "%d cars",
            "@300": "you've got %d cars"
        }
    }
}

I think this is still easily readable and maintanable.

tomkowz commented 9 years ago

I wrote loader. Working on integrating it with the framework.

tomkowz commented 9 years ago

The code is pretty much done. I am adding documentation comments and hopefully will release new version soon.

tomkowz commented 9 years ago

Swifternalization 1.2 released.