Skip to content

Commit 1536853

Browse files
committed
fix(examples): update string quoting and rename params for localization bundle calls
docs(helpers): document parse_swgoh_string and its extended tag grammar chore: add commitlint config and ignore .pythonrc.py fix(helpers): extend parse_swgoh_string to cover full NGUI tag set (#83)
1 parent 72f68a8 commit 1536853

9 files changed

Lines changed: 822 additions & 126 deletions

File tree

.commitlintrc.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"extends": ["@commitlint/config-conventional"],
3+
"rules": {
4+
"type-enum": [2, "always", [
5+
"feat", "fix", "refactor", "build", "deps",
6+
"chore", "docs", "test", "style", "ci", "perf"
7+
]],
8+
"scope-enum": [1, "always", [
9+
"core", "helpers", "deps", "release", "ci"
10+
]],
11+
"subject-max-length": [1, "always", 100]
12+
}
13+
}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ tests/exhaustive/
5353
scripts/
5454
data/
5555
gameData.json
56+
.pythonrc.py
5657

5758
# Unit test / coverage reports
5859
htmlcov/

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ repos:
2020
hooks:
2121
- id: ruff
2222
args: [--fix]
23+
exclude: ^examples/
2324
- id: ruff-format
2425

2526
# ── Mypy type checking (mirrors CI type-check job) ────────────────────

docs/api/helpers.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,56 @@ Functions for querying omicron skill data from game data collections.
321321

322322
---
323323

324+
## Localization Helpers
325+
326+
Utilities for working with the SWGOH client's BBCode-style markup that appears
327+
throughout localization bundles (ability descriptions, mod descriptions, event
328+
banners, etc.).
329+
330+
### parse_swgoh_string
331+
332+
Parse a raw localization string and convert it to plain text, ANSI-colored
333+
terminal output, Discord markdown, or HTML. The parser follows
334+
`NGUIText.ParseSymbol()` semantics, so it handles the same tag family the game
335+
engine itself supports.
336+
337+
```python
338+
from swgoh_comlink.helpers import parse_swgoh_string
339+
340+
raw = "[c][FF0000][b]Boss[/b][-] deals [u]2x[/u] damage[/c]"
341+
342+
parse_swgoh_string(raw) # 'Boss deals 2x damage'
343+
parse_swgoh_string(raw, output="discord") # '**Boss** deals __2x__ damage'
344+
parse_swgoh_string(raw, output="web") # HTML with <b>, <u>, <span style=...>
345+
parse_swgoh_string(raw, output="terminal") # ANSI truecolor escapes
346+
```
347+
348+
Supported markup:
349+
350+
| Tag(s) | Purpose |
351+
|--------|---------|
352+
| `[c] [/c] [-c]` | Optional color block wrapper |
353+
| `[-]` | Reset the active color |
354+
| `[RGB]` / `[RGBA]` / `[RRGGBB]` / `[RRGGBBAA]` | Hex color literal (short forms duplicate each nibble) |
355+
| `[A]` | 1-digit hex alpha (reuses the previous RGB or white) |
356+
| `[b] [/b]` / `[i] [/i]` | Bold / italic |
357+
| `[u] [/u]` / `[s] [/s]` | Underline / strikethrough |
358+
| `[t] [/t]` | Sprite color marker (stripped in text output) |
359+
| `[sub] [sub=X] [/sub]` / `[sup] [sup=X] [/sup]` | Subscript / superscript with optional scale |
360+
| `[y=X] [/y]` | Font scaling (web output uses inline `font-size`) |
361+
| `\n` | Literal backslash-n escape -> newline |
362+
363+
The `[c]...[/c]` wrapper is optional — bare `[FF0000]` takes effect on its
364+
own, and `[-]` clears the active color whether or not you're inside a `[c]`
365+
block.
366+
367+
::: swgoh_comlink.helpers._localization.parse_swgoh_string
368+
options:
369+
show_root_heading: true
370+
show_root_full_path: false
371+
372+
---
373+
324374
## Decorators
325375

326376
### func_timer

examples/Async/get_location_bundle.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ async def main():
2525
# Get the language bundle using the latest game data language version
2626
# By default, Comlink compresses the data and encodes it into a BASE64 string for smaller payload and
2727
# faster delivery. The result is a string value that must be decoded before using.
28-
location_bundle = await comlink.get_localization_bundle(id=game_data_versions['language'])
28+
location_bundle = await comlink.get_localization_bundle(localization_id=game_data_versions["language"])
2929

3030
# Decode the Base64 result
31-
loc_bundle_decoded = base64.b64decode(location_bundle['localizationBundle'])
31+
loc_bundle_decoded = base64.b64decode(location_bundle["localizationBundle"])
3232

3333
# Create a zipfile object to access the compressed content
3434
zip_obj = zipfile.ZipFile(io.BytesIO(loc_bundle_decoded))
@@ -48,10 +48,10 @@ async def main():
4848
# it as a string. You could also use the various other zipfile methods to extract all files to disk
4949
# (see https://docs.python.org/3/library/zipfile.html for more details), or loop through the namelist()
5050
# output and select only specific languages you are interested in.
51-
eng_obj = zip_obj.read('Loc_ENG_US.txt')
51+
eng_obj = zip_obj.read("Loc_ENG_US.txt")
5252

5353
# Decode to string then split into individual lines
54-
eng_obj_decoded = eng_obj.decode('utf-8')
54+
eng_obj_decoded = eng_obj.decode("utf-8")
5555
eng_obj_lines = eng_obj_decoded.splitlines()
5656

5757
"""
@@ -60,7 +60,7 @@ async def main():
6060
above.
6161
"""
6262
location_bundle_unzipped = await comlink.get_localization_bundle(
63-
id=game_data_versions['language'], unzip=True
63+
localization_id=game_data_versions["language"], unzip=True
6464
)
6565

6666
"""

examples/Sync/get_location_bundle.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
get_location_bundle.py
33
Script to illustrate the basic usage of the swgoh_comlink wrapper library
44
"""
5+
56
# import the SwgohComlink class from the swgoh_comlink module
6-
from swgoh_comlink import SwgohComlink
77
import base64
8-
import zipfile
98
import io
9+
import zipfile
10+
11+
from swgoh_comlink import SwgohComlink
1012

1113
# create an instance of a SwgohComlink object
1214
comlink = SwgohComlink()
@@ -20,10 +22,10 @@
2022
# Get the language bundle using the latest game data language version
2123
# By default, Comlink compresses the data and encodes it into a BASE64 string for smaller payload and faster delivery.
2224
# The result is a string value that must be decoded before using.
23-
location_bundle = comlink.get_localization_bundle(localization_id=game_data_versions['language'])
25+
location_bundle = comlink.get_localization_bundle(localization_id=game_data_versions["language"])
2426

2527
# Decode the Base64 result
26-
loc_bundle_decoded = base64.b64decode(location_bundle['localizationBundle'])
28+
loc_bundle_decoded = base64.b64decode(location_bundle["localizationBundle"])
2729

2830
# Create a zipfile object to access the compressed content
2931
zip_obj = zipfile.ZipFile(io.BytesIO(loc_bundle_decoded))
@@ -32,8 +34,8 @@
3234
"""
3335
Sample output:
3436
35-
['Loc_CHS_CN.txt', 'Loc_CHT_CN.txt', 'Loc_ENG_US.txt', 'Loc_FRE_FR.txt', 'Loc_GER_DE.txt', 'Loc_IND_ID.txt',
36-
'Loc_ITA_IT.txt', 'Loc_JPN_JP.txt', 'Loc_Key_Mapping.txt', 'Loc_KOR_KR.txt', 'Loc_POR_BR.txt', 'Loc_RUS_RU.txt',
37+
['Loc_CHS_CN.txt', 'Loc_CHT_CN.txt', 'Loc_ENG_US.txt', 'Loc_FRE_FR.txt', 'Loc_GER_DE.txt', 'Loc_IND_ID.txt',
38+
'Loc_ITA_IT.txt', 'Loc_JPN_JP.txt', 'Loc_Key_Mapping.txt', 'Loc_KOR_KR.txt', 'Loc_POR_BR.txt', 'Loc_RUS_RU.txt',
3739
'Loc_SPA_XM.txt', 'Loc_THA_TH.txt', 'Loc_TUR_TR.txt']
3840
3941
"""
@@ -43,24 +45,24 @@
4345
# You could also use the various other zipfile methods to extra all files to disk
4446
# (see https://docs.python.org/3/library/zipfile.html for more details), or loop through the namelist()
4547
# output and select only specific languages you are interested in.
46-
eng_obj = zip_obj.read('Loc_ENG_US.txt')
48+
eng_obj = zip_obj.read("Loc_ENG_US.txt")
4749

4850
# Decode to string then split into individual lines
49-
eng_obj_decoded = eng_obj.decode('utf-8')
51+
eng_obj_decoded = eng_obj.decode("utf-8")
5052
eng_obj_lines = eng_obj_decoded.splitlines()
5153

5254
"""
53-
Alternatively, if you elected to have Comlink send an unzipped response, the result is a dictionary containing keys
55+
Alternatively, if you elected to have Comlink send an unzipped response, the result is a dictionary containing keys
5456
for all of the language files (similar to the namelist() output from the zipfile method above.
5557
"""
56-
location_bundle_unzipped = comlink.get_localization_bundle(id=game_data_versions['language'], unzip=True)
58+
location_bundle_unzipped = comlink.get_localization_bundle(localization_id=game_data_versions["language"], unzip=True)
5759

5860
"""
5961
Each key of the result dictionary is a string that can be split into individual lines, or written to files.
6062
6163
>>> location_bundle_unzipped.keys()
62-
dict_keys(['Loc_CHS_CN.txt', 'Loc_CHT_CN.txt', 'Loc_ENG_US.txt', 'Loc_FRE_FR.txt', 'Loc_GER_DE.txt', 'Loc_IND_ID.txt',
63-
'Loc_ITA_IT.txt', 'Loc_JPN_JP.txt', 'Loc_Key_Mapping.txt', 'Loc_KOR_KR.txt', 'Loc_POR_BR.txt', 'Loc_RUS_RU.txt',
64+
dict_keys(['Loc_CHS_CN.txt', 'Loc_CHT_CN.txt', 'Loc_ENG_US.txt', 'Loc_FRE_FR.txt', 'Loc_GER_DE.txt', 'Loc_IND_ID.txt',
65+
'Loc_ITA_IT.txt', 'Loc_JPN_JP.txt', 'Loc_Key_Mapping.txt', 'Loc_KOR_KR.txt', 'Loc_POR_BR.txt', 'Loc_RUS_RU.txt',
6466
'Loc_SPA_XM.txt', 'Loc_THA_TH.txt', 'Loc_TUR_TR.txt'])
6567
6668
Depending on the speed of your connection and other resource factors, the time needed to retrieve the localization

examples/Sync/get_location_bundle_adv.py

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
Script to illustrate a more advanced and efficient usage of the swgoh_comlink
44
library to collect and parse the localization bundle.
55
"""
6+
67
import base64
7-
import zipfile
88
import io
9-
import os
109
import json
10+
import os
11+
import zipfile
1112
from pathlib import Path
1213

1314
from swgoh_comlink import SwgohComlink
@@ -30,11 +31,11 @@
3031

3132

3233
def parse_loc_zip(
33-
zf: zipfile.ZipFile,
34-
output_dir: str | Path = ".",
35-
delimiter: str = "|",
36-
encoding: str = "utf-8",
37-
) -> None:
34+
zf: zipfile.ZipFile,
35+
output_dir: str | Path = ".",
36+
delimiter: str = "|",
37+
encoding: str = "utf-8",
38+
) -> None:
3839
"""
3940
Parses the content of a zip file containing delimited text files, transforms the data into a
4041
key-value JSON structure, and saves the result to specified output files.
@@ -65,18 +66,19 @@ def parse_loc_zip(
6566
result = {}
6667

6768
# Process the zip file entry as a stream to conserve memory and extract the delimited text
68-
with zf.open(info) as raw:
69-
with io.TextIOWrapper(raw, encoding=encoding) as stream:
70-
for line in stream:
71-
if line.startswith("#"):
72-
continue
73-
# Use partition() instead of split() to split the line into key and value
74-
key, sep, value = line.rstrip("\r\n").partition(delimiter)
75-
# Only store the key-value pair if the delimiter is present
76-
if sep:
77-
# Note: many of the "values" contain BBCode style formatting directives
78-
# that could be stripped out before storing them in the JSON
79-
result[key] = value
69+
with zf.open(info) as raw, io.TextIOWrapper(raw, encoding=encoding) as stream:
70+
for line in stream:
71+
if line.startswith("#"):
72+
continue
73+
# Use partition() instead of split() to split the line into key and value
74+
key, sep, value = line.rstrip("\r\n").partition(delimiter)
75+
# Only store the key-value pair if the delimiter is present
76+
if sep:
77+
# Note: many of the "values" contain BBCode style formatting directives
78+
# that can be stripped or converted before storing them. See
79+
# `swgoh_comlink.helpers.parse_swgoh_string` for a parser that
80+
# emits plain text, Discord, terminal ANSI, or HTML output.
81+
result[key] = value
8082

8183
with open(output_path, "w", encoding="utf-8") as f:
8284
json.dump(result, f, ensure_ascii=False)
@@ -89,10 +91,10 @@ def parse_loc_zip(
8991

9092
print("Fetching localization bundle (id=%s)", remote_lang)
9193
location_bundle = comlink.get_localization_bundle(
92-
localization_id=remote_lang,
93-
)
94+
localization_id=remote_lang,
95+
)
9496

95-
loc_bundle_decoded = base64.b64decode(location_bundle['localizationBundle'])
97+
loc_bundle_decoded = base64.b64decode(location_bundle["localizationBundle"])
9698

9799
parse_loc_zip(zipfile.ZipFile(io.BytesIO(loc_bundle_decoded)), _LANG_DIR)
98100
print("Localization bundle parsed to %s", _LANG_DIR)

0 commit comments

Comments
 (0)