Skip to content

Commit

Permalink
Locale support
Browse files Browse the repository at this point in the history
Improve customization options
  • Loading branch information
DrorHarari committed Nov 16, 2019
1 parent cb9614a commit 3aeaaad
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 31 deletions.
29 changes: 18 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ You don't need to remember the measure names - a list of measure names will be o

![Example: see mass measure units and their conversion rules](images/example-measure.png?raw=true)

## Customizing Existing Conversions ##
## Customizing Conversions ##

You can edit the Cvt.ini configuration file and add units to existing measures or add aliases. To do so, just add a section based on the following pattern:
You can add new conversion units and aliases to existing units via the Cvt.ini configuration file. To do so, just add a section based on the following pattern:

```
[unit/{measure name}/{unit name}]
Expand All @@ -55,7 +55,7 @@ offset = {number to substract after multiplying by factor}
inverse = {if true use the inverse of the factor}
```

For example, to add a "Finger" distance unit which is equivalent to 2 cm with the alias 'fg' we can use the following definition (note that for Distance, the main unit is meter as can be seen by typing DISTANCE+<tab> in Keypirinha)
For example, to add a "Finger" distance unit which is equivalent to 2 cm with the alias 'fg' we can use the following definition (note that for Distance, the reference unit is Meter as it has the conversion factor of 1.0 as can be seen by typing DISTANCE+<tab> in Keypirinha).

```
[unit/Distance/Finger]
Expand All @@ -65,18 +65,22 @@ aliases = fg

To add an alias "hdm" for Centimeters unit of distance measure use the following (note that the unit must be specified with exdact case):
```
[unit/distance/Centimetres]
aliases = hdm
```

## Customizing Conversions ##
## Adding New Conversions Measures ##

Cvt lets you customize the measures and units it supports. To customize the list, enter the "Cvt: Customize coversions" action in the box - this will place a copy of the conversion definition file cvtdefs.json in the user configuration directory (`Keypirinha\portable\Profile\User`). Make your changes to the measure or units definitions and then enter "Cvt: Reload custom coversions" action in the box.
While one can add measures via the Cvt.ini file, if adding new measures with a lot of units, it is better to do it by means of a custom conversion definition file. Cvt maintains an internal conversion definition file called cvtdefs.json and you can export it to the User folder by entering the ```"Cvt: Customize coversions"``` action in the box. This will place a copy of the conversion definition file ```cvtdefs.json``` in the User configuration directory (```Keypirinha\portable\Profile\User```). Rename this file, say to ```my-conversions.json``` and edit it, keeping the structure but leaving only the new measures you want to add. Then tell Cvt about it by adding a ```definitions``` items to the ```[main]``` section as in:

When a custom conversion file is used, the built-in conversion file is ignored so you won't see new measures and units that come with Cvt.
```
[main]
definition =
my-conversions.json
another-conversion.json
```

If you want to create a conversion definition file that will add measures for a specific locale, you can use the name ```cvtdefs-{locale-name}.json```', for example the file ``cvtdefs-ja_JP.json``` will be loaded in addition to what's in ```cvtdefs.json' when running on Japanese machine.
If you want to create a conversion definition file that will add measures for a specific locale, you can use the name ```cvtdefs-{locale-name}.json```', for example the file ``cvtdefs-ja_JP.json``` will be loaded in addition to what's loaded by other means when running on Japanese machine.

## Installation ##

Expand All @@ -97,13 +101,16 @@ For manual installation simply download the cvt.keypirinha-package file from the
* Thanks [Shuzo Iwasaki](https://github.com/shuGH) for internationalization improvements

## Release Notes ##
**V2.1.0**
- Added ```definitions``` configuration item in the ```[main]``` section of ```Cvt.ini``` to add custom conversion definition files.
- Added ```format``` configuration item in the ```[main]``` section of ```Cvt.ini``` to allow Cvt to use locale-specific numeric formatting on input and output. Valid values are ```common``` (the default) and ```local``` to use current locale settings.

**V2.0.0**
- BERAKING CHANGE. The format of the cvtdefs.json was simplified to enable conversion customizations via the Cvt.ini configuration file. The conversion from the old format is simple but in most cases, if the customization was just about adding some units, then the now just those units need to be added.
- New ``cvtdefs-{locale-name}.json```` pattern was added.
- New ```cvtdefs-{locale-name}.json``` pattern was added.
- New Cvt.ini boolean configuration item 'debug' added to the main section to troubleshoot conversion definition.
- New Cvt.ini string configuration item 'locale' added to the main section to control the locale-specific version of ````cvtdefs-{locale}.json`` to load.
- Now it is possible to customize existing conversions using the Cvt.ini configuration file (see there for examples).
- New Cvt.ini string configuration item ```locale``` added to the main section to control the locale-specific version of ```cvtdefs-{locale}.json``` to load.
- Now it is possible to customize existing conversions and add new ones using the ```Cvt.ini``` configuration file (see there for examples).

**V1.0.3**
- Units with uppercase name where not matched on input. Fixed.
Expand Down
35 changes: 29 additions & 6 deletions cvt.ini
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,36 @@
# Troubleshooting conversions (useful when trying to customize)
#debug = false

# Can Cvt load a user provided measurement definition file?
# Cvt can work with numbers formatted according the active locale. The options are:
# * common
# The is the default, locale is ignored. The decimal separator is a period and
# no thousands separator is used.
# * local
# Numbers are parsed and displayed according to the current locale. When copying
# the result to the clipboard, no thousands separator is produced, just the decimal
# separator.
#
# * Cvt uses a built-in measurement definition file called cvtdefs.json
# * You can place a customized version of this file in the User folder
# * If you add such file, a Reload command is added for convenience
# * It is useful for extending and experimenting with Cvt
# * If you have generally useful measurements to add - let me know on Cvt's github
#format=common

# Adding custom measurements and units
#
# Cvt uses a built-in measurement definition file called cvtdefs.json
# You obtain a copy in the User folder by entering the "Cvt: Customize
# coversions" action in the Keypirinha box. You can customize this file
# and as long as it exists, Cvt will use it rather than the built-in
# one.
#
# The recommended way to customize measurements is to add a 'definitions'
# item in the main section pointing to a conversion definition file to
# load. Note that conversion definitions loaded via this 'definition'
# item are added to the existing definition, they are not replacing them.
#
# The Reload command is added for convenience to update the measurements
# when the definition was changed outside.
#
# definitions=
# custom-file-1.json
# custom-file-2.json

# Adding a custom measure is possible by adding a section to this file in the
# following format:
Expand Down
61 changes: 47 additions & 14 deletions cvt.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,24 @@ class Cvt(kp.Plugin):

CVTDEF_FILE = "cvtdefs.json"
CVTDEF_LOCALE_FILE = "cvtdefs-{}.json"
# Input parser definition
RE_NUMBER = r'(?P<number>[-+]?[0-9]+(?:\.?[0-9]+)?(?:[eE][-+]?[0-9]+)?)'
RE_FROM = r'(?P<from>[a-zA-Z]+[a-zA-Z0-9/]*)'
DONE_FROM = r'(?P<done_from>[^a-zA-Z0-9/]+)'
RE_TO = r'(?P<to>[a-zA-Z]+[a-zA-Z0-9/]*)'
DONE_TO = r'(?P<done_to>[^a-zA-Z0-9/]+)'
INPUT_PARSER = f'^{RE_NUMBER}(?=[^0-9])\s*{RE_FROM}?{DONE_FROM}?{RE_TO}?{DONE_TO}?'

def __init__(self):
super().__init__()

def get_input_parser(self, decimal_sep):
if decimal_sep != ",":
decimal_sep = "\."

# Input parser definition
RE_NUMBER = f'(?P<number>[-+]?[0-9]+(?:{decimal_sep}?[0-9]+)?(?:[eE][-+]?[0-9]+)?)'
RE_FROM = r'(?P<from>[a-zA-Z]+[a-zA-Z0-9/]*)'
DONE_FROM = r'(?P<done_from>[^a-zA-Z0-9/]+)'
RE_TO = r'(?P<to>[a-zA-Z]+[a-zA-Z0-9/]*)'
DONE_TO = r'(?P<done_to>[^a-zA-Z0-9/]+)'
INPUT_PARSER = f'^{RE_NUMBER}(?=[^0-9])\s*{RE_FROM}?{DONE_FROM}?{RE_TO}?{DONE_TO}?'

return re.compile(INPUT_PARSER)

def read_defs(self, defs_file):
defs = None
try:
Expand Down Expand Up @@ -135,20 +142,36 @@ def reconfigure(self):
self.customized_config = False
self.dbg("CVT: Reloading. Debug enabled")

# For debug locale issues
#locale.setlocale(locale.LC_ALL,f'nl_NL.utf-8')

self.format = self.settings.get_enum("format", "main", fallback="common", enum=["common","local"], case_sensitive=False, unquote=True)
locale_name = self.settings.get("locale", "main", locale.getdefaultlocale()[0])

self.decimal_sep = "."
if self.format == "local":
self.decimal_sep = f"{1.2:n}"[1] # See how 1.2 if locale-formatted

self.input_parser = self.get_input_parser(self.decimal_sep)

self.all_units = {}
self.measures = {}

defs = self.read_defs(self.CVTDEF_FILE)
if defs:
self.add_defs(defs)

locale_name = self.settings.get_bool("locale", "main", locale.getdefaultlocale()[0])

locale_specific_def = self.CVTDEF_LOCALE_FILE.format(locale_name)
defs = self.read_defs(locale_specific_def)
if defs:
self.add_defs(defs)

self.definitions = self.settings.get_multiline("definitions", "main", fallback=[], keep_empty_lines=False)
for definition_files in self.definitions:
defs = self.read_defs(definition_files)
if defs:
self.add_defs(defs)

defs = self.read_setting_defs()
if defs:
self.add_defs(defs)
Expand All @@ -161,7 +184,6 @@ def evaluate_expr(self, expr):
return 1

def on_start(self):
self.input_parser = re.compile(self.INPUT_PARSER)
self.safeparser = Parser()

self.reconfigure()
Expand Down Expand Up @@ -282,7 +304,12 @@ def on_suggest(self, user_input, items_chain):
return

# We have a number and (maybe) units
in_number = float(parsed_input["number"])
matched_number = parsed_input["number"]

if self.decimal_sep != ".":
matched_number = matched_number.replace(self.decimal_sep, ".")

in_number = float(matched_number)
in_from = parsed_input["from"]
if in_from:
in_from = in_from.lower()
Expand Down Expand Up @@ -327,14 +354,20 @@ def on_suggest(self, user_input, items_chain):

self.dbg(f"Added unit = {unit_name}")
converted = self.do_conversion(in_number, from_unit, unit)
if self.format == "common":
converted_clipboard = converted_display = f"{converted:.5g}"
else:
converted_display = locale.format_string("%.5g", converted, grouping=True)
converted_clipboard = locale.format_string("%.5g", converted, grouping=False)

suggestions.append(self.create_item(
category=self.ITEMCAT_RESULT,
label=format(converted,".5g"),
label= converted_display,
short_desc=f'{unit_name} ({",".join(unit["aliases"])})',
target=format(converted,".5g"),
target=converted_display,
args_hint=kp.ItemArgsHint.FORBIDDEN,
hit_hint=kp.ItemHitHint.IGNORE,
data_bag=repr(converted)))
data_bag=converted_clipboard))

self.set_suggestions(suggestions, kp.Match.ANY, kp.Sort.NONE)

Expand Down

0 comments on commit 3aeaaad

Please sign in to comment.