Skip to content

Commit

Permalink
Merge pull request #30 from Krutyi-4el/develop
Browse files Browse the repository at this point in the history
Version 0.12.0
  • Loading branch information
solaluset authored Nov 8, 2023
2 parents 7a8b4dc + 5bc7d18 commit fe66491
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 12 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

### Hint: use `https://github.com/Krutyi-4el/i18nice/compare/v<version 1 (older)>...v<version 2 (newer)>` to see full code difference between versions

### v0.12.0
- Added `use_locale_dirs` setting
- Improved file search
- Config reloading is now more consistent

### v0.11.1
- Fixed #25
- (pb) Invalid placeholders will always emit an error
Expand Down
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,38 @@ However we would like to use this i18n .json file in our Python sub-project or m

i18n.set('skip_locale_root_data', True)

### Different directory structure
If your tree of translation files looks similar to this, you can enable `use_locale_dirs` to get the files properly loaded:

```
locales
├── en-US
│   ├── common.yml
│   └── gui
│   ├── page1.yml
│   └── page2.yml
└── fr-FR
├── common.yml
└── gui
├── page1.yml
└── page2.yml
```

Example code:

```python
import i18n

i18n.load_path.append("locales")
i18n.set("file_format", "yml")
i18n.set("filename_format", "{namespace}.{format}")
i18n.set("skip_locale_root_data", True)
i18n.set("use_locale_dirs", True)

print(i18n.t("common.text1", locale="en-US"))
print(i18n.t("gui.page1.title", locale="en-US"))
```

### Lists

It's possible to use lists of translations, for example:
Expand Down
10 changes: 9 additions & 1 deletion i18n/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,18 @@
'plural_few': 5,
'skip_locale_root_data': False,
"enable_memoization": True,
'argument_delimiter': '|'
"argument_delimiter": "|",
"use_locale_dirs": False,
}


if "set" in globals():
# deja vu, we've just been in this place before
from . import formatters

_reload(formatters)


def set(key: str, value: Any) -> None:
"""
Sets config value
Expand Down
37 changes: 27 additions & 10 deletions i18n/resource_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,16 @@ def load_everything(locale: Optional[str] = None, *, lock: bool = False) -> None
raise I18nLockedError("Translations were locked, use unload_everything() to unlock")

for directory in config.get("load_path"):
recursive_load_everything(directory, "", locale)
if config.get("use_locale_dirs"):
for locale_dir in os.listdir(directory):
if locale and locale_dir != locale:
continue
locale_dir_path = os.path.join(directory, locale_dir)
if not os.path.isdir(locale_dir_path):
continue
recursive_load_everything(locale_dir_path, "", locale_dir)
else:
recursive_load_everything(directory, "", locale)

if not lock:
return
Expand Down Expand Up @@ -192,6 +201,8 @@ def search_translation(key: str, locale: str) -> bool:
splitted_key = key.split(config.get('namespace_delimiter'))
namespace = splitted_key[:-1]
for directory in config.get("load_path"):
if config.get("use_locale_dirs"):
directory = os.path.join(directory, locale)
recursive_search_dir(namespace, "", directory, locale)
return translations.has(key, locale)

Expand All @@ -203,18 +214,24 @@ def recursive_search_dir(
locale: str,
) -> None:
namespace = splitted_namespace[0] if splitted_namespace else ""
seeked_file = config.get("filename_format").format(
namespace=namespace,
locale=locale,
format=config.get("file_format"),
seeked_file = os.path.join(
directory,
config.get("filename_format").format(
namespace=namespace,
locale=locale,
format=config.get("file_format"),
),
)
dir_content = os.listdir(os.path.join(root_dir, directory))
if seeked_file in dir_content:
load_translation_file(os.path.join(directory, seeked_file), root_dir, locale)
elif namespace in dir_content:
if os.path.isfile(os.path.join(root_dir, seeked_file)):
return load_translation_file(seeked_file, root_dir, locale)

if not namespace:
return
namespace = os.path.join(directory, namespace)
if os.path.isdir(os.path.join(root_dir, namespace)):
recursive_search_dir(
splitted_namespace[1:],
os.path.join(directory, namespace),
namespace,
root_dir,
locale,
)
Expand Down
26 changes: 26 additions & 0 deletions i18n/tests/loader_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,25 @@ def test_load_everything_lock(self):

i18n.unload_everything()

@unittest.skipUnless(json_available, "json library not available")
def test_use_locale_dirs(self):
resource_loader.init_json_loader()
config.set("file_format", "json")
config.set("filename_format", "{namespace}.{format}")
config.set("skip_locale_root_data", True)
config.set("use_locale_dirs", True)

self.assertEqual(t("d.not_a_dict", locale="bar"), ())
try:
i18n.reload_everything(lock=True)
self.assertEqual(t("d.not_a_dict", locale="bar"), ())
self.assertIn("nested_dict_json", translations.container)
i18n.unload_everything()
i18n.load_everything("bar", lock=True)
self.assertNotIn("nested_dict_json", translations.container)
finally:
i18n.unload_everything()

def test_multilingual_caching(self):
resource_loader.init_python_loader()
config.set("enable_memoization", True)
Expand All @@ -264,6 +283,13 @@ def test_load_file_with_strange_encoding(self):
self.assertIn("ほげ", data)
self.assertEqual("ホゲ", data["ほげ"])

def test_seeked_file_is_dir(self):
i18n.register_loader(i18n.Loader, ("",))
config.set("filename_format", "{namespace}")

# should not try to load directory
t("bar.idk")

def test_get_namespace_from_filepath_with_filename(self):
tests = {
"foo": "foo.ja.yml",
Expand Down
3 changes: 3 additions & 0 deletions i18n/tests/translation_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,6 @@ def test_argument_delimiter_change(self):
def test_placeholder_delimiter_change(self):
config.set('placeholder_delimiter', '$')
self.assertEqual(t('foo.hi2', name='Bob'), 'Hello Bob !')
# should revert changes back
self.setUpClass()
self.assertNotEqual(t('foo.hi2', name='Bob'), 'Hello Bob !')
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

setup(
name='i18nice',
version="0.11.1",
version="0.12.0",
description='Translation library for Python',
long_description=long_description,
long_description_content_type='text/markdown',
Expand Down

0 comments on commit fe66491

Please sign in to comment.