Quick Links: Download Gideros Studio | Gideros Documentation | Gideros community chat | DONATE
Attempt on Localization — Gideros Forum

Attempt on Localization

ar2rsawseenar2rsawseen Maintainer
edited May 2012 in Code snippets
Hello,

wanted to try something on localization in Gideros and here is the result of my attempt.

http://appcodingeasy.com/Gideros-Mobile/Localization-in-Gideros

Basically it loads language constants for user's locale or default locale, and provides methods to simply access constant values, format them printf style, or get ready to use Text objects or images

Any comments, suggestions, criticism is appreaciated. And if anyone find it useful, feel free to use in your projects.

+1 -1 (+5 / -0 )Share on Facebook
«1345

Comments

  • bowerandybowerandy Guru
    edited May 2012
    Hi @ar2rsawseen, great post.

    It's interesting that you should post this now as I was just thinking about something similar. These are my thoughts so far just in case you'd like to incorporate any such ideas into your own Locale code.

    The two differences I was considering compared with your implementation are:

    1) Instead of requiring the code to be written with localisation in mind I was planning to allow existing code to "just work". The way I'd do this is by redirecting existing API functions that take strings to my localised versions. This would be done in Locale:init().

    2) In addition, in order to use unmodified code, I was planning to index into the locale tables using the original strings themselves. I think this should be quite fast enough since I imagine that Lua uses hash tables internally to perform the table lookups (@atilim?). Also, it's usually going to be a one off time penalty that happens on scene load rather than every frame update.

    Here's a demo I knocked up this morning after seeing your post. First time you tap it will run the showFields() function with no locale. Notice this is just normal Gideros code not written with any localisation in mind. Next time you tap it forces a french locale and runs the function again. the third time it uses the current locale (which won't exist unless you are in UK, US or FR). If a definition for the current locale does not exist then it defaults to en_US.

    Let me know what you think.
     
    BhLocale = Core.class()
     
    local en_US = {}
    en_US["Hello"]="Hello"
    en_US["Goodbye %s"]="Goodbye %s"
     
    local fr_FR = {}
    fr_FR["Hello"]="Bonjour"
    fr_FR["Goodbye %s"]="Au revoir %s"
     
    local allLocales = {
    	en_US=en_US,
    	en_GB=en_US,
    	fr_FR=fr_FR 
    	}
     
    function BhLocale:init(forceLocale)
    	-- Get the current locale, if unsupported default to en_US
    	local locale=forceLocale or application:getLocale()
    	BhLocale.current=allLocales[locale] or allLocales[en_US]
    	print("Installing locale ", locale)
     
    	-- For all functions accepting strings replace with our localized versions
    	-- In this demo we just use string.format() and TextField.new()
    	if not(BhLocale.isRedirected) then
    		string.__format=string.format
    		string.format=BhLocale.formatString
     
    		TextField.__new=TextField.new
    		TextField.new=BhLocale.newTextField
     
    		BhLocale.isRedirected=true
    	end
    end
     
    function BhLocale.formatString(fmt, ...)
    	-- Localized replacement for string.format()
    	local lfmt=BhLocale.current[fmt] or fmt
    	return string.__format(lfmt, ...)
    end
     
    function BhLocale.newTextField(font, text)
    	-- Localized replacement for TextField.new()
    	local ltext=BhLocale.current[text] or text
    	return TextField.__new(font, ltext)
    end
     
    function clearStage()
    	local numChildren=stage:getNumChildren()
    	for i=1,numChildren do
    		stage:getChildAt(1):removeFromParent()
    	end
    end
     
    function showFields()
    	-- This is our demo function, written as normal Gideros Lua code
    	local font=TTFont.new("tahoma.ttf", 15)
    	local tf=TextField.new(font, "Hello")
    	stage:addChild(tf)
    	tf:setPosition(100, 100)
     
    	local tf2=TextField.new(font, "")
    	stage:addChild(tf2)
    	tf2:setPosition(200, 100)
    	tf2:setText(string.format("Goodbye %s", "Andy"))
    end
     
    local taps=0
    local locale
     
    local function onMouseDown()
    	taps=(taps+1) % 3
    	clearStage()
    	if taps==1 then
    		-- Just show with no locale
    		showFields()
    	end
    	if taps==2 then
    		-- Show with French locale forced
    		locale=BhLocale.new("fr_FR")
    		showFields()
    	end
    	if taps==0 then
    		-- Show with default locale installed
    		locale=BhLocale.new()
    		showFields()
    	end
    end
     
    stage:addEventListener(Event.MOUSE_DOWN, onMouseDown)

    Likes: chipster123, avo

    +1 -1 (+2 / -0 )Share on Facebook
  • ar2rsawseenar2rsawseen Maintainer
    Wow, that's a great thought.
    I wasn't sure it is possible to override other classes' constructors in such a way, but it works, Thank you for this tip, I'll experiment a little more with it. ;)
  • ar2rsawseenar2rsawseen Maintainer
    Ok, here's my second attempt based on @bowerandy suggestions.

    Now object Localize is global and non-instantiable, meaning, you don't need to create an instance. That is why it is now completely seamlessly integrated, simply include it in the project.

    Plus it provides a method, which allows easy overriding of some optional objects, like Sound, URL and any other, to also support seamless localization.

    Updated docs and sample project here:
    http://appcodingeasy.com/Gideros-Mobile/Localization-in-Gideros

    Likes: bowerandy

    +1 -1 (+1 / -0 )Share on Facebook
  • bowerandybowerandy Guru
    edited May 2012
    Hi @ar2rsawseen. That looks great; even better than how I had imagined it might work. I really like the neat, generic method you use to override the various localizable functions. And look how small the Localize module is now - about 35 lines - very cool!

    I think the only thing I would have done differently is to not use a traditional Lua "module" but to stick within the object-oriented paradigm and make the load function a static function of a class:
    Localize = Core.class()
    function Localize.load(object, func, index) etc
    I guess the end result is much the same.

    Best regards

    PS: I think the video on your site is still the original and not updated for your new method.
  • Actually, one thought does occur to me as to why you might want to have Localize as an instantiable class like before. Some games may want to allow the user to dynamically select the locale (e.g. by clicking a language button) after the program has started. I don't think you can do this with your module approach, is that true?

    Best regards
  • ar2rsawseenar2rsawseen Maintainer
    Well I was thinking a lot about it, especially that using module + package.seeall seems to be a bad practice. But completely seamless integration seemed too tempting, just had to try it out.
    But yeah, it's not possible to set custom location, mostly because of code execution order.
    I guess instantiable approach might have been better.

    I'd only like to know one thing:
    how it's handled internally? and which is better for performance?

    Does anybody know?
  • bowerandybowerandy Guru
    edited May 2012
    I doubt there's much difference in terms of performance especially since the rest of Gideros is based on classes. Also I think "module" is deprecated in Lua 5.2 isn't it?

    I agree that the seamless approach is enticing but you could still do that by treating Localize as a singleton class and automatically instantiating the default instance using the current local. Then if the programmer wanted to override the default localizer they only need to replace this default instance with another one. E.g. in Localize.lua:
    Localize=Core.class()
     
    function Localize:init(locale) {
        -- Set up instance based on locale
        .. 
        -- Save singleton
        Localize.current=self
    }
    Localize.new(application:getLocale())
    Then when the "French" button is pressed in your app:
    function MyApp:onFrenchPressed()
           Localize.new("fr_FR")
    end
    Best regards
  • I downloaded the localize folder, compiled and run it but it gives the error :
    Localize.lua:30: assertion failed!
    stack traceback:
    What should i do?
  • @loves_oi is it from example project?

    The error is because the localization file does not exist. My fault, I should have done the fallback to default localization file. I'll fix it ASAP. Thank you ;)

  • Ok. We should thank you ;)
  • You can re-download try it now, it should show default text provided if no locale file found ;)
  • Ok.It ran without no problem. when i changed the format to russian , it gives the correct output.when i changed the format to latvian(latvia) - this is the example in the video - it gives output in english. When i changed format to any other lang. it still gives the output in english. It works only for russian lang.?
  • I might forgot to include Latvian locale after testing. is it still there in the project, do you see it in locales directory in Gideros Studio?

    It should accept all available locale files, not only russian :)
  • There are two files in locale folder:
    1.lv_LV
    2.ru_RU
  • And inside Gideros Studio too?
    I just checked and it works.

    Well you can experiment by creating new locale files (same format as existing) and adding them to Gideros project inside Gideros Studion in locales folder. :)
  • Is there any way to make it works with AlertDialog? Because it doesn't work with them now.
  • @Atilim, Isn't the displaying of the text dependent on the font being used?

    I guess that might be the reason why some languages display English rather than the unicode language selected in the text string. Most non unicode languages would work just fine if the characters are part of the ASCII set.
    twitter: @ozapps | http://www.oz-apps.com | http://howto.oz-apps.com | http://reviewme.oz-apps.com
    Author of Learn Lua for iOS Game Development from Apress ( http://www.apress.com/9781430246626 )
    Cool Vizify Profile at https://www.vizify.com/oz-apps
  • ar2rsawseenar2rsawseen Maintainer
    edited April 2013
    @unlying I've pushed new version of Localize supporting multiple argument override, thus it also supports AlertDialog and TextInputDialog now ;)

    https://github.com/ar2rsawseen/Localize

    Likes: unlying

    +1 -1 (+1 / -0 )Share on Facebook
  • I'm a bit confused.. I know the newer version is different than the video and example on your website. However, when you are working with self it doesn't call the correct language. I've used the xCode simulator and the image does not change.

    I've added: localize.lua, textwrap.lua (although I don't use it) , three different localized files located in the locale/ folder .


    For example here is my localized Spanish file named: es_ES.lua
    local l = {}
     
     
    l["gfx/menulisten_en.png"] = "gfx/menulisten_es.png"
     
    return l

    I call it in one of my lua pages
    Localize.path = "locales"
    Localize.filetype = "lua"
     
     
    local up = Bitmap.new(TextureRegion.new(Texture.new("gfx/menulisten_en.png", true)))
     
    Localize.load(Sound, "new", 1)
    --URLs
    Localize.load(UrlLoader, "new", 1)
    Am I missing addig something here? I've also called the localize path inside of the main.lua but nothing changes.

    Should I be adding this below to the localized file? Defining the "up" in
    local l up = {}
     
     
    l["gfx/menulisten_en.png"] = "gfx/menulisten_es.png"
     
    return l
    I received an error from adding the up..


    I know it should work and I've followed the example. Should it derive from the main.lua instead of a scenecontroller page?
  • MellsMells Guru
    edited October 2013
    Side note :
    which is the latest version? My guess is that the first one is the right one, but if a user goes to see the documentation at AppCodingEasy then downloads, he is not directed to the GitHub version.

    (by the way how do you make tests to see if the class works, I don't know how to application:setLocale() so the english version is always displayed in the code sample?)
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • @Mells :
    Did you mean to test the application:getLocale() ?
    You just need to change device's language at Setting.


    Also, for iOS, you might need to generate that *language*.lproj and add the language accordingly, so itunesconnect can detect which language is supported for your app.
  • @tkhnoman -- maybe that is my problem? i think that i only have the english default lproj loaded. makes sense

    I'll try
  • @mysps: after uploading, you can check whether it was correctly set or not at Binary Details > Localization.
  • myspsmysps Member
    edited October 2013
    I checked.. and checked again. here is a screenshot of my xcode setup. I don't know what you mean by binary details.

    Screen Shot 2013-10-30 at 9.44.31 PM.png
    936 x 472 - 163K
  • tkhnomantkhnoman Member
    edited October 2013
    It is on itunesconnect.apple.com
    After uploading, when you see App for each version, there is Binary Details at the right, below the Promo Codes. That is what i mean.
  • @tkhnoman I wanted to be able to test very quickly different locales but did not know where too look in order to set a different locale (in the simulator for instance).
    I thought there was a way to force a locale directly inside Gideros, but it seems that it's not possible?
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • Changed the link in the post, latest version is at github
    To test different locales, you could do something like:
    function application:getLocale()
        return "lv_LV"
    end
    inside init.lua file in your Gideros project (which you can create if you don't have one)
  • @ar2rsawseen who is this answer for? Can you package the localization into an xcode project because its weird that it doesnt work for me.

    Changed the link in the post, latest version is at github
    To test different locales, you could do something like:
    function application:getLocale()
        return "lv_LV"
    end
    inside init.lua file in your Gideros project (which you can create if you don't have one)
Sign In or Register to comment.