Skip to content

Commit

Permalink
Add localization helper script
Browse files Browse the repository at this point in the history
  • Loading branch information
NCBM committed Aug 30, 2024
1 parent 4a4af1f commit 5d7c00e
Show file tree
Hide file tree
Showing 2 changed files with 194 additions and 0 deletions.
192 changes: 192 additions & 0 deletions based_build/localization_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# pyright: reportImplicitOverride=false, reportUninitializedInstanceVariable=false

from __future__ import annotations

import json
from pathlib import Path
from typing import TYPE_CHECKING, Any, ClassVar, Dict, TypedDict

from rich.highlighter import ReprHighlighter
from rich.text import Text
from textual import work
from textual.app import App, ComposeResult
from textual.color import Color
from textual.screen import Screen
from textual.widgets import Footer, Header, Label, Tabs, Tree

if TYPE_CHECKING:
from textual.widgets.tree import TreeNode

highlighter = ReprHighlighter()

LOCFILES_BASE = (
Path(__file__).parent.parent / "packages" / "pyright-internal" / "src" / "localization"
)

ALL_LANGUAGES = [t for x in LOCFILES_BASE.iterdir() if (t := x.stem[12:])]


class _LocMsgComment(TypedDict):
message: str
comment: str


LocMessages = Dict[str, "str | _LocMsgComment"]


def get_locfile(language: str = "en-us"):
return LOCFILES_BASE / f"package.nls.{language}.json"


with get_locfile().open() as f:
LOCDATA_EN_US: Dict[str, LocMessages] = json.load(f) # noqa: UP006


def diff_keys(orig: dict[str, Any], comp: dict[str, Any]):
msgs: list[str] = []
orig_set = set(orig.keys())
comp_set = set(comp.keys())
if orig_set != comp_set:
msgs.append("Compared to the original:")
if missing := orig_set - comp_set:
msgs.append("these keys are missing: " + ", ".join(missing) + ".")
if unused := comp_set - orig_set:
msgs.append("these keys are unused: " + ", ".join(unused) + ".")
else:
msgs.append("This object has the same keys as the original.")
return "\n".join(msgs)


class LocDataTree(Tree[str]):
_locnode: ClassVar[Dict[str, TreeNode[str]]] = {} # noqa: UP006
temp_comp = None
lang: str

def compose(self) -> ComposeResult:
self.show_root = False
yield from super().compose()

@staticmethod
def add_msgline(node: TreeNode[str], key: str, val: str):
label = Text.assemble(Text.from_markup(f"[b]{key}[/b] = "), highlighter(repr(val)))
node.data = key
node.set_label(label)

def add_node(self, name: str, data: LocMessages, node: TreeNode[str] | None = None) -> None:
"""Adds a node to the tree.
Args:
----
name (str): Name of the node.
node (TreeNode): Parent node.f"{{}} {name}"
data (object): Data associated with the node.
"""
if name in self._locnode:
node = self._locnode[name]
node.remove_children()
if node is None:
node = self.root.add(name, name)
self._locnode[name] = node
node.set_label(Text(f"{{}} {name}"))
for key, value in data.items():
new_node = node.add_leaf("")
if isinstance(value, str):
self.add_msgline(new_node, key, value)
else:
self.add_msgline(new_node, key, value["message"])

def load_data(self, lang: str = "en-us"):
self.lang = lang
with get_locfile(lang).open() as f:
data: dict[str, LocMessages] = json.load(f)
for dom, dat in data.items():
self.add_node(dom, dat)

def on_tree_node_selected(self, event: Tree.NodeSelected[str]) -> None:
if self.lang == "en-us":
return
selected = event.node
if selected.data is None or selected == self.temp_comp:
return
parent = selected.parent
if not parent or parent.data is None:
return
if self.temp_comp is not None:
self.temp_comp.remove()
self.temp_comp = parent.add_leaf("", after=selected)
msg = LOCDATA_EN_US[parent.data][selected.data]
if isinstance(msg, str):
self.add_msgline(self.temp_comp, f"{'':>{len(selected.data) - 7}}\\[en-us]", msg)
else:
self.add_msgline(
self.temp_comp, f"{'':>{len(selected.data) - 7}}\\[en-us]", msg["message"]
)


class MsgDiffReport(Screen[None]):
BINDINGS: ClassVar = [("c", "dismiss()")]
lang = "en-us"

def compose(self) -> ComposeResult:
self.styles.align = "center", "middle"
self.report = Label()
self.report.styles.width = "70%"
self.report.styles.height = "70%"
self.report.styles.border = "solid", Color.parse("gray")
self.report.border_title = "Message Keys Difference Report"
self.compare()
yield self.report

def compare(self):
if self.lang == "en-us":
self.report.renderable = "'en-us' is already the original file."
return
with get_locfile(self.lang).open() as f:
data: dict[str, LocMessages] = json.load(f)
msgs: list[str] = []
for origdom, origmsgs in LOCDATA_EN_US.items():
msgs += ["", f"[{origdom}]", diff_keys(origmsgs, data.get(origdom, {}))]
self.report.renderable = "\n".join(msgs)


class HelperTUI(App[None]):
BINDINGS: ClassVar = [
("q", "quit()", "Quit program"),
("d", "toggle_dark", "Toggle dark mode"),
("c", "popup_keydiff", "Compare message keys differences"),
]
TITLE = "BasedPyright Localization Helper"

def compose(self) -> ComposeResult:
"""Create child widgets for the app."""
yield Header()
yield Footer()
self.tabs = Tabs(*ALL_LANGUAGES)
yield self.tabs
self.loctree: LocDataTree = LocDataTree("loctree")
self.loctree.load_data(self.tabs.active_tab.label_text if self.tabs.active_tab else "en-us")
yield self.loctree

def action_toggle_dark(self) -> None:
"""An action to toggle dark mode."""
self.dark = not self.dark

@work
async def action_popup_keydiff(self) -> None:
self.keydiff = MsgDiffReport()
self.keydiff.lang = self.tabs.active_tab.label_text if self.tabs.active_tab else "en-us"
self.install_screen(self.keydiff, "keydiff") # pyright: ignore[reportUnknownMemberType]
await self.push_screen_wait(self.keydiff)
_ = self.uninstall_screen(self.keydiff) # pyright: ignore[reportUnknownMemberType]

def on_tabs_tab_activated(self) -> None:
if self.loctree.temp_comp:
self.loctree.temp_comp.remove()
self.loctree.temp_comp = None
self.loctree.load_data(self.tabs.active_tab.label_text if self.tabs.active_tab else "en-us")


if __name__ == "__main__":
app = HelperTUI()
app.run()
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dev = [
]
# these deps are also needed in build-system.requires. see https://github.com/pdm-project/pdm/issues/2947
docstubs = ["docify>=1.0.0"]
lochelper = ["textual>=0.78.0"]

[tool.pdm.version]
source = "call"
Expand All @@ -32,6 +33,7 @@ ruff_fix = { composite = ['ruff check --fix', 'ruff format --fix'] }
pylint = 'pylint basedpyright based_build pdm_build.py'
test_python = 'pytest tests'
generate_docstubs = { call = "based_build.generate_docstubs:main" }
localization_helper = 'python based_build/localization_helper.py'

[tool.pdm.build]
excludes = ["tests/", "pdm_build.py"]
Expand Down

0 comments on commit 5d7c00e

Please sign in to comment.