Skip to content

Commit 108c3a4

Browse files
committed
Plural values export to Twine
1 parent 96f0719 commit 108c3a4

File tree

4 files changed

+135
-20
lines changed

4 files changed

+135
-20
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[[Bookmarks]]
2+
[bookmarks_places]
3+
comment = Number of bookmarks on UI
4+
en:one = %d bookmark
5+
en:other = %d bookmarks
6+
ru:one = %d метка
7+
ru:few = %d метки
8+
ru:many = %d меток
9+
ru:other = %d меток
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
"""
2+
Tests for formatter classes.
3+
"""
4+
5+
from pathlib import Path
6+
7+
import pytest
8+
9+
from twine.twine_file import TwineFile, TwineDefinition, TwineSection
10+
11+
12+
class TestTwineFilePlural:
13+
@pytest.fixture
14+
def fixtures_dir(self):
15+
"""Get fixtures directory path."""
16+
return Path(__file__).parent / "fixtures"
17+
18+
def test_write_plural_format(self):
19+
import tempfile
20+
21+
twine_file = TwineFile()
22+
num_edits_def = TwineDefinition("num_edits")
23+
24+
twine_file.definitions_by_key["num_edits"] = num_edits_def
25+
twine_file.language_codes.append("en")
26+
twine_file.sections = [TwineSection("OSM")]
27+
twine_file.sections[0].definitions.append(num_edits_def)
28+
29+
# Put plural translations
30+
num_edits_def.plural_translations["en"] = {
31+
"one": "%d edit",
32+
"other": "%d edits"
33+
}
34+
35+
# Export
36+
with tempfile.TemporaryDirectory() as tmpdirname:
37+
tmp_twine_path = tmpdirname + "/plural.txt"
38+
twine_file.write(tmp_twine_path)
39+
with open(tmp_twine_path, "rt") as fout:
40+
twine_content = fout.read()
41+
42+
assert twine_content == """[[OSM]]
43+
\t[num_edits]
44+
\t\ten:one = %d edit
45+
\t\ten:other = %d edits
46+
"""
47+
48+
def test_read_plurals(self, fixtures_dir):
49+
twine_file = TwineFile()
50+
twine_file.read(str(fixtures_dir / "twine_plural_values.txt"))
51+
52+
assert twine_file.language_codes == ["en", "ru"]
53+
54+
assert "bookmarks_places" in twine_file.definitions_by_key
55+
definition = twine_file.definitions_by_key["bookmarks_places"]
56+
57+
assert "en" in definition.translations
58+
assert definition.translations["en"] == "%d bookmarks" # "en:other" value is copied to translations
59+
assert "en" in definition.plural_translations
60+
assert definition.plural_translations["en"] == {
61+
"one": "%d bookmark",
62+
"other": "%d bookmarks"
63+
}
64+
65+
assert "ru" in definition.translations
66+
assert definition.translations["ru"] == "%d меток" # "ru:other" value is copied to translations
67+
assert "ru" in definition.plural_translations
68+
assert definition.plural_translations["ru"] == {
69+
"one": "%d метка",
70+
"few": "%d метки",
71+
"many": "%d меток",
72+
"other": "%d меток"
73+
}

python_twine/tests/test_twine_file.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,15 +191,32 @@ def test_write_file(self):
191191
with open(temp_path, "r") as f:
192192
content = f.read()
193193

194-
assert "[[Test]]" in content
195-
assert "[test_key]" in content
196-
assert "en = Test" in content
197-
assert "es = Prueba" in content
198-
assert "comment = A test string" in content
199-
assert "tags = test" in content
194+
assert content == """[[Test]]
195+
\t[test_key]
196+
\t\tcomment = A test string
197+
\t\ttags = test
198+
\t\ten = Test
199+
\t\tes = Prueba
200+
"""
200201
finally:
201202
Path(temp_path).unlink()
202203

203204

205+
class TestWriter:
206+
@pytest.fixture
207+
def fixtures_dir(self):
208+
"""Get fixtures directory path."""
209+
return Path(__file__).parent / "fixtures"
210+
211+
def test_accent_symbol(self, fixtures_dir):
212+
twine_file = TwineFile()
213+
twine_file.read(str(fixtures_dir / "twine_accent_values.txt"))
214+
assert twine_file.definitions_by_key["value_with_leading_accent"].translations["en"] == '`value'
215+
assert twine_file.definitions_by_key["value_with_trailing_accent"].translations["en"] == 'value`'
216+
assert twine_file.definitions_by_key["value_with_leading_space"].translations["en"] == ' value'
217+
assert twine_file.definitions_by_key["value_with_trailing_space"].translations["en"] == 'value '
218+
assert twine_file.definitions_by_key["value_wrapped_by_spaces"].translations["en"] == ' value '
219+
assert twine_file.definitions_by_key["value_wrapped_by_accents"].translations["en"] == '`value`'
220+
204221
if __name__ == "__main__":
205222
pytest.main([__file__, "-v"])

python_twine/twine/twine_file.py

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -346,17 +346,33 @@ def _write_value(
346346
self, definition: TwineDefinition, language: str, file
347347
) -> Optional[str]:
348348
"""Write a single translation value to file."""
349-
value = definition.translations.get(language)
350-
if not value:
351-
return None
352-
353-
# Wrap in backticks if starts/ends with space or already has backticks
354-
if (
355-
value.startswith(" ")
356-
or value.endswith(" ")
357-
or (value.startswith("`") and value.endswith("`"))
358-
):
359-
value = f"`{value}`"
360-
361-
file.write(f"\t\t{language} = {value}\n")
362-
return value
349+
output = None
350+
if language in definition.plural_translations:
351+
# Write plurals:
352+
# ru:one = %d метка
353+
# ru:few = %d метки
354+
# ru:many = %d меток
355+
# ru:other = %d меток
356+
output = ""
357+
for quantity, value in definition.plural_translation_for_lang(language).items():
358+
output += f"\t\t{language}:{quantity} = {escape_spaces_backticks(value)}\n"
359+
360+
elif language in definition.translations:
361+
singular_value = definition.translations[language]
362+
# Write singular
363+
output = f"\t\t{language} = {escape_spaces_backticks(singular_value)}\n"
364+
365+
if output:
366+
file.write(output)
367+
return output
368+
369+
370+
def escape_spaces_backticks(txt: str) -> str:
371+
# Wrap in backticks if starts/ends with space or already has backticks
372+
if (
373+
txt.startswith(" ")
374+
or txt.endswith(" ")
375+
or (txt.startswith("`") and txt.endswith("`"))
376+
):
377+
return f"`{txt}`"
378+
return txt

0 commit comments

Comments
 (0)