Merge pull request #2203 from zhaoweny/i18n

I18n: more zh_CN translations and a better locale/README.md
This commit is contained in:
Ryan Holmes
2020-06-20 15:01:13 -04:00
committed by GitHub
10 changed files with 1196 additions and 154 deletions

View File

@@ -41,6 +41,7 @@ cipher = None
clientHash = None
experimentalFeatures = None
version = None
language = None
ESI_CACHE = 'esi_cache'

View File

@@ -14,8 +14,8 @@ from service.fit import Fit
from utils.cjk import isStringCjk
from .events import FitSelected, SearchSelected, ImportSelected, Stage1Selected, Stage2Selected, Stage3Selected
pyfalog = Logger(__name__)
_ = wx.GetTranslation
class NavigationPanel(SFItem.SFBrowserItem):
@@ -45,20 +45,20 @@ class NavigationPanel(SFItem.SFBrowserItem):
self.recentBmp = self.AdjustChannels(self.recentBmpH)
self.newBmp = self.AdjustChannels(self.newBmpH)
self.toolbar.AddButton(self.resetBmp, "Ship groups", clickCallback=self.OnHistoryReset,
self.toolbar.AddButton(self.resetBmp, _("Ship groups"), clickCallback=self.OnHistoryReset,
hoverBitmap=self.resetBmpH)
self.toolbar.AddButton(self.rewBmp, "Back", clickCallback=self.OnHistoryBack, hoverBitmap=self.rewBmpH)
self.btnNew = self.toolbar.AddButton(self.newBmp, "New fitting", clickCallback=self.OnNewFitting,
self.toolbar.AddButton(self.rewBmp, _("Back"), clickCallback=self.OnHistoryBack, hoverBitmap=self.rewBmpH)
self.btnNew = self.toolbar.AddButton(self.newBmp, _("New fitting"), clickCallback=self.OnNewFitting,
hoverBitmap=self.newBmpH, show=False)
self.btnSwitch = self.toolbar.AddButton(self.switchBmpD, "Hide empty ship groups",
self.btnSwitch = self.toolbar.AddButton(self.switchBmpD, _("Hide empty ship groups"),
clickCallback=self.ToggleEmptyGroupsView, hoverBitmap=self.switchBmpH,
show=False)
self.btnRecent = self.toolbar.AddButton(self.recentBmpD, "Recent Fits",
self.btnRecent = self.toolbar.AddButton(self.recentBmpD, _("Recent Fits"),
clickCallback=self.ToggleRecentShips, hoverBitmap=self.recentBmpH,
show=True)
modifier = "CTRL" if 'wxMac' not in wx.PlatformInfo else "CMD"
self.toolbar.AddButton(self.searchBmp, "Search fittings ({}+F)".format(modifier), clickCallback=self.ToggleSearchBox,
self.toolbar.AddButton(self.searchBmp, _("Search fittings") + " ({}+F)".format(modifier), clickCallback=self.ToggleSearchBox,
hoverBitmap=self.searchBmpH)
self.padding = 4
@@ -70,7 +70,7 @@ class NavigationPanel(SFItem.SFBrowserItem):
w, h = size
self.BrowserSearchBox = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition,
(-1, h - 2 if 'wxGTK' in wx.PlatformInfo else -1),
(wx.BORDER_NONE if 'wxGTK' in wx.PlatformInfo else 0))
(wx.BORDER_NONE if 'wxGTK' in wx.PlatformInfo else 0))
self.BrowserSearchBox.Show(False)
# self.BrowserSearchBox.Bind(wx.EVT_TEXT_ENTER, self.OnBrowserSearchBoxEnter)
@@ -144,11 +144,11 @@ class NavigationPanel(SFItem.SFBrowserItem):
def ToggleEmptyGroupsView(self):
if self.shipBrowser.filterShipsWithNoFits:
self.shipBrowser.filterShipsWithNoFits = False
self.btnSwitch.label = "Hide empty ship groups"
self.btnSwitch.label = _("Hide empty ship groups")
self.btnSwitch.normalBmp = self.switchBmpD
else:
self.shipBrowser.filterShipsWithNoFits = True
self.btnSwitch.label = "Show empty ship groups"
self.btnSwitch.label = _("Show empty ship groups")
self.btnSwitch.normalBmp = self.switchBmp
stage = self.shipBrowser.GetActiveStage()

View File

@@ -58,7 +58,7 @@ class ResistancesViewFull(StatsView):
# Custom header EHP
headerContentSizer = self.headerPanel.Parent.GetHeaderContentSizer()
self.stEff = wx.StaticText(headerPanel, wx.ID_ANY, "( Effective HP: ")
self.stEff = wx.StaticText(headerPanel, wx.ID_ANY, "(" + _("Effective HP") + ": ")
headerContentSizer.Add(self.stEff)
headerPanel.GetParent().AddToggleItem(self.stEff)
@@ -66,7 +66,7 @@ class ResistancesViewFull(StatsView):
headerContentSizer.Add(self.labelEhp, 0)
headerPanel.GetParent().AddToggleItem(self.labelEhp)
stCls = wx.StaticText(headerPanel, wx.ID_ANY, " )")
stCls = wx.StaticText(headerPanel, wx.ID_ANY, ")")
headerPanel.GetParent().AddToggleItem(stCls)
headerContentSizer.Add(stCls)

View File

@@ -340,7 +340,7 @@ class SkillTreeView(wx.Panel):
self.skillBookDirtyImageId = self.imageList.Add(wx.Icon(BitmapLoader.getBitmap("skill_small_red", "gui")))
tree.AppendColumn(_("Skill"))
tree.AppendColumn(_("Level"), align=wx.ALIGN_CENTER)
tree.AppendColumn(_("Level"))
# tree.SetMainColumn(0)
self.root = tree.GetRootItem()

View File

@@ -24,7 +24,7 @@ from gui.bitmap_loader import BitmapLoader
from gui.utils import color as color_utils, draw, fonts
from service.fit import Fit
_ = wx.GetTranslation
_PageChanging, EVT_NOTEBOOK_PAGE_CHANGING = wx.lib.newevent.NewEvent()
_PageChanged, EVT_NOTEBOOK_PAGE_CHANGED = wx.lib.newevent.NewEvent()
_PageAdding, EVT_NOTEBOOK_PAGE_ADDING = wx.lib.newevent.NewEvent()
@@ -215,7 +215,8 @@ class ChromeNotebook(wx.Panel):
wx.PostEvent(self, PageChanged(current_page, new_page))
def AddPage(self, win=None, title="Empty Tab", image: wx.Image=None, closeable=True):
def AddPage(self, win=None, title=None, image: wx.Image=None, closeable=True):
title = title or "Empty Tab"
if self._active_page:
self._active_page.Hide()

View File

@@ -37,7 +37,8 @@ class MultiSwitch(ChromeNotebook):
if h:
h(type, info)
def AddPage(self, tabWnd=None, tabTitle=_("Empty Tab"), tabImage=None):
def AddPage(self, tabWnd=None, tabTitle=None, tabImage=None):
tabTitle = tabTitle or _("Empty Tab")
if tabWnd is None:
tabWnd = gui.builtinViews.emptyView.BlankPage(self)
tabWnd.handleDrag = lambda type, info: self.handleDrag(type, info)

View File

@@ -19,7 +19,9 @@ from service.market import Market
pyfalog = Logger(__name__)
_ = wx.GetTranslation
_t = wx.GetTranslation
class AttributeEditor(AuxiliaryFrame):
def __init__(self, parent):
@@ -142,8 +144,8 @@ class AttributeEditor(AuxiliaryFrame):
def OnClear(self, event):
with wx.MessageDialog(
self,
_("Are you sure you want to delete all overrides?"),
_("Confirm Delete"),
_t("Are you sure you want to delete all overrides?"),
_t("Confirm Delete"),
wx.YES | wx.NO | wx.ICON_EXCLAMATION
) as dlg:
if dlg.ShowModal() == wx.ID_YES:

View File

@@ -3,6 +3,8 @@ import gui.mainFrame
import webbrowser
import gui.globalEvents as GE
_t = wx.GetTranslation
class SsoLogin(wx.Dialog):
@@ -10,12 +12,12 @@ class SsoLogin(wx.Dialog):
mainFrame = gui.mainFrame.MainFrame.getInstance()
super().__init__(
mainFrame, id=wx.ID_ANY, title=_("SSO Login"), style=wx.DEFAULT_DIALOG_STYLE,
size=wx.Size(450, 240) if "wxGTK" in wx.PlatformInfo else wx.Size(400, 240))
mainFrame, id=wx.ID_ANY, title=_t("SSO Login"), style=wx.DEFAULT_DIALOG_STYLE,
size=wx.Size(450, 240) if "wxGTK" in wx.PlatformInfo else wx.Size(400, 240))
bSizer1 = wx.BoxSizer(wx.VERTICAL)
text = wx.StaticText(self, wx.ID_ANY, _("Copy and paste the block of text provided by pyfa.io"))
text = wx.StaticText(self, wx.ID_ANY, _t("Copy and paste the block of text provided by pyfa.io"))
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10)
self.ssoInfoCtrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, (-1, -1), style=wx.TE_MULTILINE)
@@ -44,7 +46,7 @@ class SsoLoginServer(wx.Dialog):
def __init__(self, port):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
super().__init__(self.mainFrame, id=wx.ID_ANY, title=_("SSO Login"), size=(-1, -1), style=wx.DEFAULT_DIALOG_STYLE)
super().__init__(self.mainFrame, id=wx.ID_ANY, title=_t("SSO Login"), size=(-1, -1), style=wx.DEFAULT_DIALOG_STYLE)
from service.esi import Esi
@@ -57,7 +59,7 @@ class SsoLoginServer(wx.Dialog):
self.mainFrame.Bind(GE.EVT_SSO_LOGIN, self.OnLogin)
self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
text = wx.StaticText(self, wx.ID_ANY, _("Waiting for character login through EVE Single Sign-On."))
text = wx.StaticText(self, wx.ID_ANY, _t("Waiting for character login through EVE Single Sign-On."))
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10)
bSizer3 = wx.BoxSizer(wx.VERTICAL)

View File

@@ -1,12 +1,81 @@
On my Windows wsl2 (more generic explaination is needed):
# Pyfa Internationalization(i18n) and Localization(l10n)
`find ./gui -iname "*.py" | xargs pygettext3 -d lang -o locale/lang.pot`
Below is a summary of [GNU gettext](https://www.gnu.org/software/gettext/) manual, adapted for Pyfa i18n workflow.
This will generate the pot that should be carried over to the various language directories and renamed .po
[Poedit](https://poedit.net/) offers a nice GUI for same GNU gettext translation workflow.
`msgfmt -o lang.mo lang`
## i18n with command line
Run in each language directory, will compile the .po files to .mo files
Windows users can get these tools via Git for windows, Msys2 or Cygwin; or just use WSL / WSL2.
For Linux and macOS users these tools might be avaliable out-of-box.
## Issues
`zh_CH` doesn't seem to work. AddCatalog is not functioning. See https://discuss.wxpython.org/t/localization-not-working-with-zh-ch-addcatalog-returns-false/34628
### To generate new template for translation:
```console
$ find */ *.py -name "*.py" | xgettext -o locale/lang.pot -d lang -k_t -k_r -
```
explaination:
* `find */ *.py -name "*.py"`: collect all `.py` file path in root dir and all sub-folder, write it to stdout
* `xgettext`: a utility looking for keyword and put string literals in a specific format for human translation
* `-o locale/lang.pot`: let `xgettext` write to `locale/lang.pot`
* `-d lang`: default language domain is `lang`
* `-k_t -k_r`: besides default keyword (including `_`, see `info xgettext` for detail), also look for `_t` and `_r`
* `-`: let `xgettext` to read from stdin, which is connected to `find` stdout
this `locale/lang.pot` is called PO template, which is throwed away once actual `ll_CC/LC_MESSAGES/lang.po` is ready for use.
### To initalize PO file for new language
```console
$ msginit -i locale/lang.pot -l ll_CC -o locale/ll_CC/LC_MESSAGES/lang.po
```
explaination:
* `-i locale/lang.pot`: input file location
* `-l ll_CC`: target locale. `ll` should be a language code, and `CC` should be a country code
* `-o locale/ll_CC/LC_MESSAGES/lang.po`: output file
* `ll_CC`: same as above
* `LC_MESSAGES`: GNU gettext conventional path to search for localized messages
* `lang.po`: language domain and file format
this `locale/ll_CC/LC_MESSAGES/lang.po` should be checked into VCS, later it will be converted into mechine readable format (MO).
### To update PO file for existing translation
```console
$ msgmerge locale/ll_CC/LC_MESSAGES/lang.po locale/lang.pot
```
### To do actual translation
just edit the `lang.po` file :)
### To generate mechine readable MO file
For a single locale:
```console
$ msgfmt locale/ll_CC/LC_MESSAGES/lang.po -o locale/ll_CC/LC_MESSAGES/lang.mo
```
For all avaliable locale:
```bash
for $f in locale/*/; do
msgfmt $f/LC_MESSAGES/lang.po -o $f/LC_MESSAGES/lang.mo
done
```
## i18n with Poedit
### To update PO file for existing translation
1. open a existing `locale/ll_CC/LC_MESSAGES/lang.po`
2. *Catalog* -> *Update form POT file*
3. select pre-prepared `lang.pot` file
### To translate and generate MO file
edit the translation and hit Save :)

File diff suppressed because it is too large Load Diff