Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1812] Rename the Translation Function #2209

Merged
merged 8 commits into from
May 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions EDMC.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@

if TYPE_CHECKING:
from logging import TRACE # type: ignore # noqa: F401 # needed to make mypy happy
def _(x: str): return x

edmclogger.set_channels_loglevel(logging.INFO)

Expand All @@ -35,7 +34,7 @@ def _(x: str): return x
import commodity
import companion
import edshipyard
import l10n
from l10n import translations as tr
import loadout
import outfitting
import shipyard
Expand Down Expand Up @@ -66,7 +65,7 @@ def log_locale(prefix: str) -> None:
)


l10n.Translations.install_dummy()
tr.install_dummy()

SERVER_RETRY = 5 # retry pause for Companion servers [s]
EXIT_SUCCESS, EXIT_SERVER, EXIT_CREDENTIALS, EXIT_VERIFICATION, EXIT_LAGGING, EXIT_SYS_ERR, EXIT_ARGS, \
Expand Down Expand Up @@ -164,7 +163,7 @@ def main(): # noqa: C901, CCR001
newversion: EDMCVersion | None = updater.check_appcast()
if newversion:
# LANG: Update Available Text
newverstr: str = _("{NEWVER} is available").format(NEWVER=newversion.title)
newverstr: str = tr.tl("{NEWVER} is available").format(NEWVER=newversion.title)
print(f'{appversion()} ({newverstr})')
else:
print(appversion())
Expand Down
177 changes: 87 additions & 90 deletions EDMarketConnector.py

Large diffs are not rendered by default.

14 changes: 10 additions & 4 deletions PLUGINS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1193,21 +1193,27 @@ widget if you need to display routine status information.
## Localisation

You can localise your plugin to one of the languages that EDMarketConnector
itself supports. Add the following boilerplate near the top of each source
itself supports. Add the following boilerplate near the top of the source
file that contains strings that needs translating:

```python
import l10n
import functools
_ = functools.partial(l10n.Translations.translate, context=__file__)
plugin_tl = functools.partial(l10n.translations.tl, context=__file__)

```

Wrap each string that needs translating with the `_()` function, e.g.:
Wrap each string that needs translating with the `plugin_tl()` function, e.g.:

```python
somewidget["text"] = _("Happy!")
somewidget["text"] = plugin_tl("Happy!")
```

Note that you can name the "plugin_tl" function whatever you want - just make sure to stay consistent!
Many plugins use `_` as the singleton name. We discourage that in versions 5.11 onward, but it should still work.
If your plugin has multiple files that need translations, simply import the `plugin_tl` function to that location.
You should only need to add the boilerplate once.

If you display localized strings in EDMarketConnector's main window you should
refresh them in your `prefs_changed` function in case the user has changed
their preferred language.
Expand Down
29 changes: 14 additions & 15 deletions companion.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,11 @@
from edmc_data import companion_category_map as category_map
from EDMCLogging import get_main_logger
from monitor import monitor
from l10n import translations as tr

logger = get_main_logger()

if TYPE_CHECKING:
def _(x: str): return x

UserDict = collections.UserDict[str, Any] # indicate to our type checkers what this generic class holds normally
else:
UserDict = collections.UserDict # Otherwise simply use the actual class
Expand Down Expand Up @@ -224,7 +223,7 @@ def __init__(self, *args) -> None:
self.args = args
if not args:
# LANG: Frontier CAPI didn't respond
self.args = (_("Error: Frontier CAPI didn't respond"),)
self.args = (tr.tl("Error: Frontier CAPI didn't respond"),)


class ServerConnectionError(ServerError):
Expand All @@ -243,7 +242,7 @@ def __init__(self, *args) -> None:
self.args = args
if not args:
# LANG: Frontier CAPI data doesn't agree with latest Journal game location
self.args = (_('Error: Frontier server is lagging'),)
self.args = (tr.tl('Error: Frontier server is lagging'),)


class NoMonitorStation(Exception):
Expand All @@ -259,7 +258,7 @@ def __init__(self, *args) -> None:
self.args = args
if not args:
# LANG: Commander is docked at an EDO settlement, got out and back in, we forgot the station
self.args = (_("Docked but unknown station: EDO Settlement?"),)
self.args = (tr.tl("Docked but unknown station: EDO Settlement?"),)


class CredentialsError(Exception):
Expand All @@ -269,7 +268,7 @@ def __init__(self, *args) -> None:
self.args = args
if not args:
# LANG: Generic "something went wrong with Frontier Auth" error
self.args = (_('Error: Invalid Credentials'),)
self.args = (tr.tl('Error: Invalid Credentials'),)


class CredentialsRequireRefresh(Exception):
Expand All @@ -294,7 +293,7 @@ def __init__(self, *args) -> None:
self.args = args
if not args:
# LANG: Frontier CAPI authorisation not for currently game-active commander
self.args = (_('Error: Wrong Cmdr'),)
self.args = (tr.tl('Error: Wrong Cmdr'),)


class Auth:
Expand Down Expand Up @@ -429,7 +428,7 @@ def authorize(self, payload: str) -> str: # noqa: CCR001
'<unknown error>'
)
# LANG: Generic error prefix - following text is from Frontier auth service
raise CredentialsError(f'{_("Error")}: {error!r}')
raise CredentialsError(f'{tr.tl("Error")}: {error!r}')

r = None
try:
Expand Down Expand Up @@ -472,18 +471,18 @@ def authorize(self, payload: str) -> str: # noqa: CCR001
if (usr := data_decode.get('usr')) is None:
logger.error('No "usr" in /decode data')
# LANG: Frontier auth, no 'usr' section in returned data
raise CredentialsError(_("Error: Couldn't check token customer_id"))
raise CredentialsError(tr.tl("Error: Couldn't check token customer_id"))

if (customer_id := usr.get('customer_id')) is None:
logger.error('No "usr"->"customer_id" in /decode data')
# LANG: Frontier auth, no 'customer_id' in 'usr' section in returned data
raise CredentialsError(_("Error: Couldn't check token customer_id"))
raise CredentialsError(tr.tl("Error: Couldn't check token customer_id"))

# All 'FID' seen in Journals so far have been 'F<id>'
# Frontier, Steam and Epic
if f'F{customer_id}' != monitor.state.get('FID'):
# LANG: Frontier auth customer_id doesn't match game session FID
raise CredentialsError(_("Error: customer_id doesn't match!"))
raise CredentialsError(tr.tl("Error: customer_id doesn't match!"))

logger.info(f'Frontier CAPI Auth: New token for \"{self.cmdr}\"')
cmdrs = config.get_list('cmdrs', default=[])
Expand All @@ -505,7 +504,7 @@ def authorize(self, payload: str) -> str: # noqa: CCR001
self.dump(r)

# LANG: Failed to get Access Token from Frontier Auth service
raise CredentialsError(_('Error: unable to get token')) from e
raise CredentialsError(tr.tl('Error: unable to get token')) from e

logger.error(f"Frontier CAPI Auth: Can't get token for \"{self.cmdr}\"")
self.dump(r)
Expand All @@ -514,7 +513,7 @@ def authorize(self, payload: str) -> str: # noqa: CCR001
'<unknown error>'
)
# LANG: Generic error prefix - following text is from Frontier auth service
raise CredentialsError(f'{_("Error")}: {error!r}')
raise CredentialsError(f'{tr.tl("Error")}: {error!r}')

@staticmethod
def invalidate(cmdr: str | None) -> None:
Expand Down Expand Up @@ -841,7 +840,7 @@ def capi_single_query(
except Exception as e:
logger.debug('Attempting GET', exc_info=e)
# LANG: Frontier CAPI data retrieval failed
raise ServerError(f'{_("Frontier CAPI query failure")}: {capi_endpoint}') from e
raise ServerError(f'{tr.tl("Frontier CAPI query failure")}: {capi_endpoint}') from e

if capi_endpoint == self.FRONTIER_CAPI_PATH_PROFILE and 'commander' not in capi_data:
logger.error('No commander in returned data')
Expand Down Expand Up @@ -874,7 +873,7 @@ def handle_http_error(response: requests.Response, endpoint: str):
if response.status_code == 418:
# "I'm a teapot" - used to signal maintenance
# LANG: Frontier CAPI returned 418, meaning down for maintenance
raise ServerError(_("Frontier CAPI down for maintenance"))
raise ServerError(tr.tl("Frontier CAPI down for maintenance"))

logger.exception('Frontier CAPI: Misc. Error')
raise ServerError('Frontier CAPI: Misc. Error')
Expand Down
22 changes: 14 additions & 8 deletions docs/Translations.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,20 @@ Translations are handled on [OneSky](https://oneskyapp.com/), specifically in [t

### Setting it up in the code

#### Call `_(...)`
#### Call `tr.tl(...)`
If you add any new strings that appear in the application UI, e.g. new configuration options, then you should specify them as:

_('Text that appears in UI')
`_()` is a special global function that then handles the translation, using its single argument, plus the configured language, to look up the appropriate text.
tr.tl('Text that appears in UI')

In order to do this, you must add the following import:

`from l10n import translations as tr`

`tr.tl()` is a function that then handles the translation, using its single argument, plus the configured language, to look up the appropriate text.

If you need to specify something in the text that shouldn't be translated then use the form:

_('Some text with a {WORD} not translated').format(WORD='word')
tr.tl('Some text with a {WORD} not translated').format(WORD='word')
This way 'word' will always be used literally.

#### Add a LANG comment
Expand All @@ -28,8 +33,9 @@ end of the line in your usage**. If both comments exist, the one on the
current line is preferred over the one above

```py
from l10n import translations as tr
# LANG: this says stuff.
_('stuff')
tr.tl('stuff')
```

#### Edit `L10n/en.template` to add the phrase
Expand All @@ -43,20 +49,20 @@ e.g.
"Authentication successful" = "Authentication successful";
which matches with:

self.status['text'] = _('Authentication successful') # Successfully authenticated with the Frontier website
self.status['text'] = tr.tl('Authentication successful') # Successfully authenticated with the Frontier website

and

/* Help text in settings. [prefs.py] */
"Tip: You can disable a plugin by{CR}adding '{EXT}' to its folder name" = "Tip: You can disable a plugin by{CR}adding '{EXT}' to its folder name";
which matches with:

nb.Label(plugsframe, text=_("Tip: You can disable a plugin by{CR}adding '{EXT}' to its folder name").format(EXT='.disabled')).grid( # Help text in settings
nb.Label(plugsframe, text=tr.tl("Tip: You can disable a plugin by{CR}adding '{EXT}' to its folder name").format(EXT='.disabled')).grid( # Help text in settings
`{CR}` is handled in `l10n.py`, translating to a unicode `\n`. See the code in`l10n.py` for any other such special substitutions.

You can even use other translations within a given string, e.g.:

_("One or more of your enabled plugins do not yet have support for Python 3.x. Please see the list on the '{PLUGINS}' tab of '{FILE}' > '{SETTINGS}'. You should check if there is an updated version available, else alert the developer that they need to update the code for Python 3.x.\r\n\r\nYou can disable a plugin by renaming its folder to have '{DISABLED}' on the end of the name.".format(PLUGINS=_('Plugins'), FILE=_('File'), SETTINGS=_('Settings'), DISABLED='.disabled'))
tr.tl("One or more of your enabled plugins do not yet have support for Python 3.x. Please see the list on the '{PLUGINS}' tab of '{FILE}' > '{SETTINGS}'. You should check if there is an updated version available, else alert the developer that they need to update the code for Python 3.x.\r\n\r\nYou can disable a plugin by renaming its folder to have '{DISABLED}' on the end of the name.".format(PLUGINS=tr.tl('Plugins'), FILE=tr.tl('File'), SETTINGS=tr.tl('Settings'), DISABLED='.disabled'))
/* Popup body: Warning about plugins without Python 3.x support [EDMarketConnector.py] */
"One or more of your enabled plugins do not yet have support for Python 3.x. Please see the list on the '{PLUGINS}' tab of '{FILE}' > '{SETTINGS}'. You should check if there is an updated version available, else alert the developer that they need to update the code for Python 3.x.\r\n\r\nYou can disable a plugin by renaming its folder to have '{DISABLED}' on the end of the name." = "One or more of your enabled plugins do not yet have support for Python 3.x. Please see the list on the '{PLUGINS}' tab of '{FILE}' > '{SETTINGS}'. You should check if there is an updated version available, else alert the developer that they need to update the code for Python 3.x.\r\n\r\nYou can disable a plugin by renaming its folder to have '{DISABLED}' on the end of the name.";

Expand Down
19 changes: 8 additions & 11 deletions journal_lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,13 @@
from enum import Enum
from os import getpid as os_getpid
from tkinter import ttk
from typing import TYPE_CHECKING, Callable

from typing import Callable
from l10n import translations as tr
from config import config
from EDMCLogging import get_main_logger

logger = get_main_logger()

if TYPE_CHECKING: # pragma: no cover
def _(x: str) -> str:
return x


class JournalLockResult(Enum):
"""Enumeration of possible outcomes of trying to lock the Journal Directory."""
Expand Down Expand Up @@ -212,7 +208,7 @@ def __init__(self, parent: tk.Tk, callback: Callable) -> None:
self.parent = parent
self.callback = callback
# LANG: Title text on popup when Journal directory already locked
self.title(_('Journal directory already locked'))
self.title(tr.tl('Journal directory already locked'))

# remove decoration
if sys.platform == 'win32':
Expand All @@ -225,16 +221,17 @@ def __init__(self, parent: tk.Tk, callback: Callable) -> None:

self.blurb = tk.Label(frame)
# LANG: Text for when newly selected Journal directory is already locked
self.blurb['text'] = _("The new Journal Directory location is already locked.{CR}"
"You can either attempt to resolve this and then Retry, or choose to Ignore this.")
self.blurb['text'] = tr.tl("The new Journal Directory location is already locked.{CR}"
"You can either attempt to resolve this and then Retry, "
"or choose to Ignore this.")
self.blurb.grid(row=1, column=0, columnspan=2, sticky=tk.NSEW)

# LANG: Generic 'Retry' button label
self.retry_button = ttk.Button(frame, text=_('Retry'), command=self.retry)
self.retry_button = ttk.Button(frame, text=tr.tl('Retry'), command=self.retry)
self.retry_button.grid(row=2, column=0, sticky=tk.EW)

# LANG: Generic 'Ignore' button label
self.ignore_button = ttk.Button(frame, text=_('Ignore'), command=self.ignore)
self.ignore_button = ttk.Button(frame, text=tr.tl('Ignore'), command=self.ignore)
self.ignore_button.grid(row=2, column=1, sticky=tk.EW)
self.protocol("WM_DELETE_WINDOW", self._destroy)

Expand Down
Loading
Loading