Support for multiple languages/external config files #71

Open
jarvisteach opened this Issue Dec 23, 2016 · 13 comments

Comments

Projects
None yet
2 participants
@jarvisteach
Owner

jarvisteach commented Dec 23, 2016

It would be great to have a simple way to load text from an external config file.
This could then be extended to support multiple languages...

The following widgets will need investigating:

  • Label
  • Entry
  • Button
  • RadioButton
  • CheckBox
  • OptionBox
  • SpinBox
  • ListBox
  • Scale
  • Message
  • TextArea
  • Meter
  • Properties
  • Separator
  • Links
  • Grip
  • DatePicker
  • MicroBit
  • GoogleMaps
  • PieChart
  • Tree
  • Grid
  • MarPlotLib
  • Images
  • Sounds
  • Menubars
  • Statusbars
  • Toolbars
  • Pop-ups
  • Tooltips
  • Splashscreen
  • LabelFrame
  • ToggleFrame
  • TabbedFrame
  • PanedFrame
  • PagedWindow
  • SubWindow
  • Frame
  • ScrollPane
  • EXTERNAL
@jarvisteach

This comment has been minimized.

Show comment
Hide comment
@jarvisteach

jarvisteach Dec 23, 2016

Owner

Python has a built-in config parser: https://docs.python.org/3.5/library/configparser.html

You set up a config file:

[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes

[bitbucket.org]
User = hg

Then, load the config file:

import configparser
config = configparser.ConfigParser()
config.read('config.ini')

Then, access the data:

config.get("DEFAULT", "Compression")
Owner

jarvisteach commented Dec 23, 2016

Python has a built-in config parser: https://docs.python.org/3.5/library/configparser.html

You set up a config file:

[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes

[bitbucket.org]
User = hg

Then, load the config file:

import configparser
config = configparser.ConfigParser()
config.read('config.ini')

Then, access the data:

config.get("DEFAULT", "Compression")
@jarvisteach

This comment has been minimized.

Show comment
Hide comment
@jarvisteach

jarvisteach Dec 23, 2016

Owner

To support multiple languages, one option is to have sections for each language, and then the same keys. The section name (language) can then be set at start time, and the text passed to each widget would be used as a lookup key, instead of text.

This would only happen if a internationalisation flag was checked.

Furthermore, a dictionary of widgets could be built with text:object pairs. If the language is changed during operation, then the dictionary is looped through, and all text is reloaded....

Owner

jarvisteach commented Dec 23, 2016

To support multiple languages, one option is to have sections for each language, and then the same keys. The section name (language) can then be set at start time, and the text passed to each widget would be used as a lookup key, instead of text.

This would only happen if a internationalisation flag was checked.

Furthermore, a dictionary of widgets could be built with text:object pairs. If the language is changed during operation, then the dictionary is looped through, and all text is reloaded....

@jarvisteach

This comment has been minimized.

Show comment
Hide comment
@jarvisteach

jarvisteach Dec 23, 2016

Owner

PLAN

Support 4 types of config data:

  • Text
  • Images
  • Sound
  • Style

These will require 2 different files:

  • text file, grouped by language
  • style file, grouped my type (image, audio, style)

The user can then set the path for each file through a function, and enable/disable each config option.
This will all have to be configured before any widgets are added.

Owner

jarvisteach commented Dec 23, 2016

PLAN

Support 4 types of config data:

  • Text
  • Images
  • Sound
  • Style

These will require 2 different files:

  • text file, grouped by language
  • style file, grouped my type (image, audio, style)

The user can then set the path for each file through a function, and enable/disable each config option.
This will all have to be configured before any widgets are added.

jarvisteach added a commit that referenced this issue Dec 26, 2016

jarvisteach added a commit that referenced this issue Dec 26, 2016

Internationalisation v0.1 (#71)
Initial support for internationalisation.

Key features:
* go() takes an optional start language, this switches on
internationalisation
* setLanguage() called by go() but offers alternative way to initiate
multi-language
* changeLanguage() allows the user to change the language of the GUI
* all widgets need to receive a DEFAULT_TEXT
* change to Link class, to capture title from config function call

jarvisteach added a commit that referenced this issue Dec 26, 2016

Internationalisation (#71)
Demo code & tweaks to unsupported widgets

jarvisteach added a commit that referenced this issue Dec 26, 2016

Unicode support (#71) & SplashScreen (#74)
Now uses codecs (utf8) to read language files, hopefully resolves
Unicode issues

Introduced new SplashScreen class, and option to show a Splash

jarvisteach added a commit that referenced this issue Dec 27, 2016

Update to internationalisation (#71)
* Now supports radio button, listbox, entry default
* fixed bugs with entry default
* fixed bugs with tick boxes not returning real booleans

jarvisteach added a commit that referenced this issue Dec 27, 2016

Update to internationalisation (#71)
Updated docs
Included new tests
@jarvisteach

This comment has been minimized.

Show comment
Hide comment
@jarvisteach

jarvisteach Dec 28, 2016

Owner

Issue with python 2.7 - ConfigParser works differently...

Owner

jarvisteach commented Dec 28, 2016

Issue with python 2.7 - ConfigParser works differently...

jarvisteach added a commit that referenced this issue Jan 7, 2017

@jarvisteach jarvisteach modified the milestone: 0.06 Feb 25, 2017

@jarvisteach jarvisteach modified the milestones: 0.07, 0.06 Apr 14, 2017

@jarvisteach jarvisteach self-assigned this May 30, 2017

@jarvisteach jarvisteach modified the milestones: 0.07, 0.08 Aug 2, 2017

jarvisteach added a commit that referenced this issue Aug 10, 2017

Updated LabelFrame (#207)
Can now set anchor & title of LabelFrames

LabelFrames now included in Internationalisation (#71) and titles.

Updated testing for above.

Also, other changes to internationalisation - updated so works in Python 2.7. Thought it was working, but can't have been - now all looks good.
@jarvisteach

This comment has been minimized.

Show comment
Hide comment
@jarvisteach

jarvisteach Aug 10, 2017

Owner

Think Internationalisation is fixed in python 2 now.

Also, configured it so that keys weren't being lowercased - not sure if that was just in python2 or not.

Owner

jarvisteach commented Aug 10, 2017

Think Internationalisation is fixed in python 2 now.

Also, configured it so that keys weren't being lowercased - not sure if that was just in python2 or not.

jarvisteach added a commit that referenced this issue Aug 10, 2017

Internationalisation ToggleFrame support (#71)
ToggleFrame now has a function setToggleFrameText()

Internationaisation can now update ToggleFrames.

jarvisteach added a commit that referenced this issue Aug 10, 2017

TabbedFrame Internationalisation (#71)
TabbedFrame now has functions to set tab names.

appJar also has a function to `.setTabText()`

So, internationalisation can now support tabbed frames.

jarvisteach added a commit that referenced this issue Aug 10, 2017

Properties Internationalisation (#71)
Properties now supports internationalisation

Can also set the text of individual property items.

jarvisteach added a commit that referenced this issue Aug 13, 2017

Splash Internationalisation #71
Added splash into TITLE group.

Moved internationalisaiton before splashscreen in GO function

jarvisteach added a commit that referenced this issue Aug 18, 2017

Toolbar internationalisation #71
Piechart & Tree are not being done.

jarvisteach added a commit that referenced this issue Aug 19, 2017

Grid Internationalisation #71
Can now translate Buttons & column heading

Also - fixed bug where many duplicate buttons were being created.

jarvisteach added a commit that referenced this issue Aug 19, 2017

jarvisteach added a commit that referenced this issue Aug 19, 2017

jarvisteach added a commit that referenced this issue Aug 19, 2017

PagedWindow Internationalisation #71
Also, started migrating PagedWindow to use a conf function for all
settings.

And renamed some functions/variables - to be more self-explanatory.

jarvisteach added a commit that referenced this issue Aug 19, 2017

Statusbar Internationalisation #71
Also, new function to `.setStatusbarHeader()`

jarvisteach added a commit that referenced this issue Aug 19, 2017

@jarvisteach

This comment has been minimized.

Show comment
Hide comment
@jarvisteach

jarvisteach Aug 19, 2017

Owner

Apparently, pop-up buttons can't be configured - although, they will change depending on the platform's language?

Furthermore, the text of pop-ups isn't stored in the app's internal widget store - it is passed as a function parameter.

Popups have no identifying feature, so a new method for on-the-fly translation will need to be implemented. In fact, this same method could be used for sounds, and perhaps images - although images do exist as widgets.

Owner

jarvisteach commented Aug 19, 2017

Apparently, pop-up buttons can't be configured - although, they will change depending on the platform's language?

Furthermore, the text of pop-ups isn't stored in the app's internal widget store - it is passed as a function parameter.

Popups have no identifying feature, so a new method for on-the-fly translation will need to be implemented. In fact, this same method could be used for sounds, and perhaps images - although images do exist as widgets.

@jarvisteach

This comment has been minimized.

Show comment
Hide comment
@jarvisteach

jarvisteach Aug 19, 2017

Owner

Images should follow the same format as other widgets - translating them will just call .setImage() with the new value.

Owner

jarvisteach commented Aug 19, 2017

Images should follow the same format as other widgets - translating them will just call .setImage() with the new value.

@jarvisteach

This comment has been minimized.

Show comment
Hide comment
@jarvisteach

jarvisteach Aug 19, 2017

Owner

The final piece to the puzzle is to store a language lookup dictionary.

This will contain a dictionary with three sections:

  • POPUPS - the key will be the popup title, the value will be a list (across 2 lines) for the new title & text.

Then, when a popup is shown, if the config dictionary exists, contains a POPUP section with a matching key, the text will be pulled from the dictionary.

  • SOUNDS - a key and filename

When a sound is played/looped - the same process as above will happen.

  • EXTERNAL - a list of as many key/value pairs as desired

A new function will be provided .translate(key, default=None) this will lookup the requested key in the EXTERNAL section and return it or the specified default.

Owner

jarvisteach commented Aug 19, 2017

The final piece to the puzzle is to store a language lookup dictionary.

This will contain a dictionary with three sections:

  • POPUPS - the key will be the popup title, the value will be a list (across 2 lines) for the new title & text.

Then, when a popup is shown, if the config dictionary exists, contains a POPUP section with a matching key, the text will be pulled from the dictionary.

  • SOUNDS - a key and filename

When a sound is played/looped - the same process as above will happen.

  • EXTERNAL - a list of as many key/value pairs as desired

A new function will be provided .translate(key, default=None) this will lookup the requested key in the EXTERNAL section and return it or the specified default.

@mpmc

This comment has been minimized.

Show comment
Hide comment
@mpmc

mpmc Aug 19, 2017

Contributor

I've been using gettext and something like this..

import gettext
import uuid
gettext.install('appname')

def lang(self, key):
    """Return string associated with key. If key doesn't exist
       a unique ID is returned.

       :param key: String key.
    """
    lang_strings = {
        'error_title': _('Something Went Wrong'),
    }
    try:
        return str(lang_strings[key.lower()])
    except KeyError:
        return str(uuid.uuid4())

# Example..        
self.errorBox(self.lang('error_title'), self.lang('error_msg'))

This obviously won't work without the rest of the code, but you get the idea..

Contributor

mpmc commented Aug 19, 2017

I've been using gettext and something like this..

import gettext
import uuid
gettext.install('appname')

def lang(self, key):
    """Return string associated with key. If key doesn't exist
       a unique ID is returned.

       :param key: String key.
    """
    lang_strings = {
        'error_title': _('Something Went Wrong'),
    }
    try:
        return str(lang_strings[key.lower()])
    except KeyError:
        return str(uuid.uuid4())

# Example..        
self.errorBox(self.lang('error_title'), self.lang('error_msg'))

This obviously won't work without the rest of the code, but you get the idea..

jarvisteach added a commit that referenced this issue Aug 19, 2017

Statusbar Internationalisation change #71
STAUTUSBAR now in TITLE group

Updates to docs

jarvisteach added a commit that referenced this issue Aug 19, 2017

@jarvisteach

This comment has been minimized.

Show comment
Hide comment
@jarvisteach

jarvisteach Aug 19, 2017

Owner

Hi @mpmc - that looks like it might have been easier than implementing my own solution!
But, as I'm nearly done, I'm gonna keep going...

The code that I've implemented is fairly independent - so it shouldn't really have an effect on anything else, hopefully gettext will continue to work as intended.

Perhaps, longer term, I should look to embed the gettext functions into appJar though...

Owner

jarvisteach commented Aug 19, 2017

Hi @mpmc - that looks like it might have been easier than implementing my own solution!
But, as I'm nearly done, I'm gonna keep going...

The code that I've implemented is fairly independent - so it shouldn't really have an effect on anything else, hopefully gettext will continue to work as intended.

Perhaps, longer term, I should look to embed the gettext functions into appJar though...

jarvisteach added a commit that referenced this issue Aug 19, 2017

Image Internationalisation #71
Also included feature to keep MouseOver working after imageUpdate

jarvisteach added a commit that referenced this issue Aug 20, 2017

Start of EXTERNAL internationalisaiton #71
Added base for 3 EXTERNAL translations.

POPUP translation working
@jarvisteach

This comment has been minimized.

Show comment
Hide comment
@jarvisteach

jarvisteach Aug 20, 2017

Owner

NB. Can't indent ini file - should be able to??

Owner

jarvisteach commented Aug 20, 2017

NB. Can't indent ini file - should be able to??

jarvisteach added a commit that referenced this issue Aug 20, 2017

EXTERNAL & SOUND Internationalisation #71
Also removed some debug messages

jarvisteach added a commit that referenced this issue Aug 20, 2017

jarvisteach pushed a commit that referenced this issue Aug 20, 2017

Richard Jarvis
Fixed issue with sound (#223) and internationalisation (#71)
Sound import was borken - fixed
The EXTERNAL block didn't finish the loop, so one set of widgets  would
get processed a second time, with the wrong SECTION i

jarvisteach added a commit that referenced this issue Aug 20, 2017

MENUBAR Internationalisation #71
Updated Internationalisation to have an IMPORT function for
ConfigParser (like other libs)

Accordingly, have removed the need to call setLanguage to initiate
internationalisation - it just calls changeLanguage()

Added some extra debug for checking ini file.

Introduced functions to rename MENU & MENUITEM

These are then called by the Internationalisation code.

It works the first time, but this changes the menu names, and it won;t
work a second time.

jarvisteach added a commit that referenced this issue Aug 20, 2017

@jarvisteach

This comment has been minimized.

Show comment
Hide comment
@jarvisteach

jarvisteach Aug 22, 2017

Owner

Would be nice for appJar to determine the platform language, and search for a language file...

Owner

jarvisteach commented Aug 22, 2017

Would be nice for appJar to determine the platform language, and search for a language file...

jarvisteach added a commit that referenced this issue Aug 22, 2017

Rename optionBox #132
Initial function to renameOptionBox items - although doesn;t yet update the var (so may disply wrong text).

Added in docstrings for OptionBox #127

Added locale lookup (#71) - appJar now sets a global variable storing the lcoale, can be used to determine a language file to use...

@jarvisteach jarvisteach modified the milestones: 0.08, 0.09 Aug 25, 2017

@jarvisteach

This comment has been minimized.

Show comment
Hide comment
@jarvisteach

jarvisteach Nov 27, 2017

Owner

This is currently broken - the new widgetStore broke the name lookup.

Owner

jarvisteach commented Nov 27, 2017

This is currently broken - the new widgetStore broke the name lookup.

jarvisteach added a commit that referenced this issue Nov 27, 2017

Quick fix to internationalisation
As mentioned in #189 & added to #71 - the new structure broke
internationalisation. Temporary fix introduced.

@TheSleepyPenguin TheSleepyPenguin referenced this issue Nov 27, 2017

Open

TTK Support #189

12 of 16 tasks complete

@jarvisteach jarvisteach modified the milestones: 0.90, 1.0 Dec 10, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment