Merge branch 'release-5.8.0' into stable

This commit is contained in:
Athanasius committed 2023-01-20 12:04:31 +00:00
commit 563efc3766
88 files changed
+11201 -9786

No files matched your search

+2 -1
View File
@@ -6,8 +6,9 @@ exclude =
dist.win32/
FDevIDs/
venv/
venv-py39/
.venv/
wix/
hotkey/darwin.py # FIXME: Check under macOS VM at some point
# Show exactly where in a line the error happened
#show-source = True
+11 -2
View File
@@ -9,10 +9,15 @@ name: Push-Checks
on:
push:
branches: [ develop ]
# We'll catch issues on `develop` or any PR branch.
branches-ignore:
- 'main'
- 'stable'
- 'releases'
- 'beta'
jobs:
build:
push_checks:
runs-on: ubuntu-22.04
@@ -59,3 +64,7 @@ jobs:
grep -E -z -Z '\.py$' | \
xargs -0 flake8 --count --statistics --extend-ignore D
#######################################################################
- name: mypy type checks
run: |
./scripts/mypy-all.sh --platform win32
+18 -32
View File
@@ -2,9 +2,10 @@
name: Submodule Updates
on:
# We might want this on a schedule once this is in `main`
push:
branches: [ develop ]
# push:
# branches: [ develop ]
schedule:
- cron: '0 12 * * *'
jobs:
check_submodules:
@@ -19,6 +20,7 @@ jobs:
steps:
- uses: actions/checkout@v3
with:
ref: '${{ env.CHECKOUT_BRANCH }}'
submodules: true
- name: Update submodules
@@ -33,50 +35,34 @@ jobs:
continue-on-error: true
run: |
changes=$(git status --porcelain)
git status --porcelain
if [ -n "${changes}" ];
then
echo '::set-output changes=true'
echo 'changes=true' >> $GITHUB_OUTPUT
echo "changes_text=${changes}" >> $GITHUB_OUTPUT
else
echo 'changes=false' >> $GITHUB_OUTPUT
fi
echo '::set-output changes=false'
exit 0
- name: Create submodules changes branch
if: steps.check_for_changes.outputs.changes == 'true'
run: |
git checkout -b $GITHUB_RUN_ID
git checkout -b "submodule-change/$GITHUB_RUN_ID" $CHECKOUT_BRANCH
git commit -am "updating submodules"
git push --set-upstream origin $GITHUB_RUN_ID
git push --set-upstream origin "submodule-change/$GITHUB_RUN_ID"
- name: Create pull request against target branch
if: steps.check_for_changes.outputs.changes == 'true'
uses: actions/github-script@v6
with:
github-token: ${{ inputs.github_token }}
github-token: "${{ secrets.GITHUB_TOKEN }}"
script: |
await github.rest.pulls.create({
owner: '${{ inputs.owner }}',
repo: '${{ inputs.parent_repository }}'.split('/')[1].trim(),
head: process.env.GITHUB_RUN_ID,
base: '${{ inputs.pr_against_branch }}',
owner: '${{ github.repository_owner }}',
repo: '${{ github.repository }}'.split('/')[1].trim(),
head: 'submodule-change/${{ github.run_id }}',
base: '${{ env.PR_AGAINST_BRANCH }}',
title: `[Auto-generated] Submodule Updates ${process.env.GITHUB_RUN_ID}`,
body: `[Auto-generated] Submodule Updates ${process.env.GITHUB_RUN_ID}`,
});
- name: Add labels
if: steps.check_for_changes.outputs.changes == 'true'
uses: actions/github-script@v6
with:
github-token: ${{ inputs.github_token }}
script: |
const res = await github.rest.issues.listForRepo({
owner: '${{ inputs.owner }}',
repo: '${{ inputs.parent_repository }}'.split('/')[1].trim(),
});
const pr = res.data.filter(i => i.title.includes(process.env.GITHUB_RUN_ID));
const prNumber = pr[0].number;
await github.rest.issues.addLabels({
issue_number: prNumber,
owner: '${{ inputs.owner }}',
repo: '${{ inputs.parent_repository }}'.split('/')[1].trim(),
labels: ['${{ inputs.label }}']
body: '${{ steps.check_for_changes.outputs.changes_text }}',
});
+83 -5
View File
@@ -1,3 +1,4 @@
# vim: tabstop=2 shiftwidth=2
name: Build EDMC for Windows
on:
@@ -7,7 +8,61 @@ on:
workflow_dispatch:
jobs:
test:
variables:
outputs:
sem_ver: ${{ steps.var.outputs.sem_ver }}
archive_exclusions: ${{ steps.var.outputs.archive_exclusions }}
runs-on: "ubuntu-latest"
steps:
- name: Setting global variables
uses: actions/github-script@v6
id: var
with:
script: |
core.setOutput('sem_ver', '${{ github.ref_name }}'.replaceAll('Release\/', ''))
linux_build:
needs: [variables]
name: Linux environment build steps
runs-on: "ubuntu-latest"
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Make tar archive
run: |
# For 'tar' we can only specify filename/glob exclusions, not any
# directory location
tar -c -v -z \
-f ../EDMarketConnector-release-${{ needs.variables.outputs.sem_ver }}.tar.gz \
-C .. \
--exclude=EDMarketConnector-release-*.* \
--exclude=.editorconfig \
--exclude=.flake8 \
--exclude=.git* \
--exclude=.mypy.ini \
--exclude=.pre-commit-config.yaml \
--exclude=Build-exe-and-msi.py \
--exclude=*.manifest \
--exclude=coriolis-data \
--exclude=img \
--exclude=pyproject.toml \
--exclude=scripts \
--exclude=tests \
--exclude=wix \
EDMarketConnector
mv ../EDMarketConnector-release-${{ needs.variables.outputs.sem_ver }}.tar.gz .
- name: Upload build files
uses: actions/upload-artifact@v3
with:
name: Built files
path: |
EDMarketConnector-release-*.tar.gz
windows_build:
needs: [variables]
name: Build EDMC
runs-on: windows-2019
@@ -20,6 +75,25 @@ jobs:
with:
submodules: true
- name: Make zip archive
uses: thedoctor0/zip-release@main
with:
type: 'zip'
# We want an in-zip prefix of `EDMarketConnector/` for all files, so
# we specify that:
# 1. We work from the parent directory
directory: '..'
# 2. The path we're using is the 'root' directory
path: 'EDMarketConnector'
# 3. The .zip file has to be in the 'root' so that upload-artifact
# will process it. Can't use relative paths.
filename: 'EDMarketConnector/EDMarketConnector-release-${{ needs.variables.outputs.sem_ver }}.zip'
# 4. And all the exclusions need to be prefixed with the 'root'
# directory.
# NB: If this gets too long it can cause zip 'Command Line Error',
# presumably due to a Windows CL length limit.
exclusions: 'EDMarketConnector/EDMarketConnector-release-*.* EDMarketConnector/.editorconfig EDMarketConnector/.flake8 EDMarketConnector/.git* EDMarketConnector/.mypy.ini EDMarketConnector/.pre-commit-config.yaml EDMarketConnector/Build-exe-and-msi.py EDMarketConnector/*.manifest EDMarketConnector/coriolis-data/ EDMarketConnector/img/ EDMarketConnector/pyproject.toml EDMarketConnector/scripts/ EDMarketConnector/tests/ EDMarketConnector/wix/'
- uses: actions/setup-python@v4
with:
python-version-file: '.python-version'
@@ -44,12 +118,14 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: Built files
path: EDMarketConnector_win*.msi
path: |
EDMarketConnector_win_*.msi
EDMarketConnector-release-*.zip
release:
name: Release new version
runs-on: ubuntu-latest
needs: test
needs: [ windows_build, linux_build ]
if: "${{ github.event_name != 'workflow_dispatch' }}"
steps:
@@ -60,7 +136,7 @@ jobs:
path: ./
- name: Hash files
run: sha256sum EDMarketConnector_win*.msi > ./hashes.sum
run: sha256sum EDMarketConnector_win_*.msi EDMarketConnector-release-*.{zip,tar.gz} > ./hashes.sum
- name: Create Draft Release
uses: "softprops/action-gh-release@v1"
@@ -70,5 +146,7 @@ jobs:
prerelease: true
discussion_category_name: "Announcement"
files: |
./EDMarketConnector_win*.msi
./EDMarketConnector_win_*.msi
./EDMarketConnector-release-*.zip
./EDMarketConnector-release-*.tar.gz
./hashes.sum
+2 -1
View File
@@ -18,7 +18,8 @@ EDMarketConnector.VisualElementsManifest.xml
.idea
.vscode
venv
.venv/
venv/
*.code-workspace
htmlcov/
.ignored
+3 -2
View File
@@ -1,9 +1,10 @@
[mypy]
follow_imports = skip
follow_imports = normal
ignore_missing_imports = True
scripts_are_modules = True
; Without this bare `mypy <file>` may get warnings for e.g.
; `<var> = <value>`
; i.e. no typing info.
check-untyped-defs = True
check_untyped_defs = True
; platform = darwin
explicit_package_bases = True
+2 -2
View File
@@ -46,12 +46,12 @@ repos:
# mypy - static type checking
# mypy --follow-imports skip <file>
- repo: https://github.com/pre-commit/mirrors-mypy
rev: 'v0.931'
rev: 'v0.991'
hooks:
- id: mypy
# verbose: true
# log_file: 'pre-commit_mypy.log'
additional_dependencies: [ types-requests ]
additional_dependencies: [ types-pkg-resources, types-requests, types-urllib3 ]
# args: [ "--follow-imports", "skip", "--ignore-missing-imports", "--scripts-are-modules" ]
### # pydocstyle.exe <file>
+4 -2
View File
@@ -2,7 +2,6 @@
"""Build to executables and MSI installer using py2exe and other tools."""
import os
import pathlib
import platform
import re
import shutil
import sys
@@ -22,12 +21,14 @@ if sys.version_info[0:2] != (3, 11):
raise AssertionError(f'Unexpected python version {sys.version}')
if sys.platform == 'win32':
assert platform.architecture()[0] == '32bit', 'A Python 32bit build is required'
import py2exe # noqa: F401 # Yes, this *is* used
dist_dir = 'dist.win32'
else:
raise AssertionError(f'Unsupported platform {sys.platform}')
# This added to make mypy happy
assert sys.platform == 'win32'
###########################################################################
###########################################################################
@@ -83,6 +84,7 @@ OPTIONS = {
'packages': [
'asyncio', # No longer auto as of py3.10+py2exe 0.11
'multiprocessing', # No longer auto as of py3.10+py2exe 0.11
'pkg_resources._vendor.platformdirs', # Necessary 2023-01-17
'sqlite3', # Included for plugins
'util', # 2022-02-01 only imported in plugins/eddn.py
],
+217 -1
View File
@@ -13,10 +13,17 @@ produce the Windows executables and installer.
**As a consequence of this we no longer support Windows 7.
This is due to
[Python 3.10.x itself not supporting Windows 7](https://www.python.org/downloads/windows/).
[Python 3.10.x and later not supporting Windows 7](https://www.python.org/downloads/windows/).
The application (both EDMarketConnector.exe and EDMC.exe) will crash on
startup due to a missing DLL.**
As [Windows 8.1 is now End-Of-Life](https://learn.microsoft.com/en-us/lifecycle/faq/windows#windows-8-1)
we no longer explicitly support it, but for the time being it will likely
continue to work. This is dependent on future Python releases not dropping
support for Windows 8.1 in a manner that prevents it working. Any bug report
made against Windows 8.1 use may be ignored unless reproduced on a supported
OS.
This should have no other impact on users or plugin developers, other
than the latter now being free to use features that were introduced since the
Python 3.7 series.
@@ -27,6 +34,215 @@ produce the Windows executables and installer.
---
Release 5.8.0
===
This release is essentially the same as 5.8.0-rc3 with only the version and
this changelog updated.
It brings a new feature related to Fleetcarrier data, some convenience for
Linux users, some fixes, and otherwise some internal changes that should not
adversely affect either users or third-party plugins. For the latter, read
below for some new/changed things that could benefit you.
* This release, and all future ones, now create two additional archive files
in the GitHub release:
1. `EDMarketConnector-release-<version>.zip`
2. `EDMarketConnector-release-<version>.tar.gz`
The advantage of these over the GitHub auto-generated ones is that they
have been hand-crafted to contain *all* the necessary files, and *only*
those files.
**If you use the application from source, and not via a git clone, then we
highly recommend you use one of these archives, not the GitHub
auto-generated ones.**
Anyone installing on Windows should continue to use the
`EDMarketConnector_win_<version>.msi` files as before.
* **New Feature** - You can now have the application query the `/fleetcarrier`
CAPI endpoint for data about your Fleet Carrier. The data will then be
passed to interested plugins.
Note that there are some caveats:
1. This feature defaults to *Off*. The option is on the Configuration tab
of Settings as "Enable Fleetcarrier CAPI Queries". **It is advised to only
enable this if you know you utilise plugins that make use of the data.**
2. These queries are *only* triggered by `CarrierBuy` and `CarrierStats`
Journal events, i.e. upon buying a Fleetcarrier or opening the Carrier
Management UI in-game. **NB: There is a 15 minute cooldown between
queries.**
3. If you have Fleetcarrier cargo which got into the cargo hold through a lot
of individual transactions, or if you have a lot of separate buy/sell
orders then these queries can take a *long* time to complete.
**If this happens with your game account then all other CAPI queries will
be blocked until the `/fleetcarrier` query completes.** 'Other CAPI
queries' means those usually triggered upon docking to gather station
market, shipyard and outfitting data. To ameliorate the effects of this
there is currently a timeout of 60 seconds on `/fleetcarrier` queries,
**and will not occur more often than every 15 minutes**.
We plan to address this by moving the `/fleetcarrier` queries into their
own separate thread in the future.
* The code for choosing the 'Output' folder is now simply the `tkinter`
function for such a dialogue, rather than a special case on Windows. In
the past the former had issues with Unicode characters, but in testing no
such issue was observed (on a supported OS).
* There are two new items on the "Help" menu:
1. Troubleshooting -> [Wiki:Troubleshooting](https://github.com/EDCD/EDMarketConnector/wiki/Troubleshooting)
2. Report A Bug -> [Issues - New Bug Report](https://github.com/EDCD/EDMarketConnector/issues/new?assignees=&labels=bug%2C+unconfirmed&template=bug_report.md&title=)
* Translations have been updated. Thanks again to our volunteer translators!
* If we ever activate any functionality killswitches, the popup denoting which
are active has been made more readable.
* There's a new section in `Contributing.md` - "Python Environment". This
should aid any new developers in getting things set up.
Linux Users
---
We now ship an `io.edcd.EDMarketConnector.desktop` file. To make use of this
you should run `scripts/linux-setup.sh` *once*. This will:
1. Check that you have `$HOME/bin` in your PATH. If not, it will abort.
2. Create a shell script `edmarketconnector` in `$HOME/bin` to launch the
application.
NB: This relies on the filesystem location you placed the source in not
changing. So if you move the source you will need to re-run the script.
3. Copy the .desktop and .icon files into appropriate locations. The .desktop
file utilises the shell script created in step 2, and thus relies on it
existing *and* on it being in a directory that is in your PATH.
Once this has been completed any XDG-compliant desktops should have an entry
for "E:D Market Connector" in their "Games" menu.
Fixes
---
* The tracking of a Cmdr's location that was being performed by the core EDDN
plugin has been moved into the Journal monitoring code. This results in
the tracking being correct upon application (re)start, reflecting the state
from the latest Journal file, rather than only picking up with any
subsequent new Journal events.
This change should remove instances of "Wrong System! Missed Jump ?" and
similar sanity-check "errors" when continuing to play after a user restarts
the application whilst the game is running.
Plugin developers, see below for how this change can positively affect you.
* The name of the files written by "File" > "Save Raw Data" now have a `.`
between the system and station names.
* Use of CAPI data in `EDMC.exe` when invoked with either `-s` or `-n`
arguments hadn't been updated for prior changes, causing such invocations to
fail. This has been fixed.
Plugin Developers
---
* Each plugin is now handed its own sub-frame as the `parent` parameter passed
to `plugin_app()` *instead of the actual main UI frame*. These new Frames
are placed in the position that plugin UI would have gone into. This should
have no side effects on well-behaved plugins.
However, if you have code that attempts to do things like `parent.children()`
or the like in your `plugin_app()` implementation, this might have stopped
working. You shouldn't be trying to do anything with any of the UI outside
your plugin *anyway*, but if you definitely have a need then look things up
using `.nametowidget()`. There are examples in the core plugins (which *DO*
have good reason, due to maintaining main UI label values).
All of the plugins listed on our Wiki were given *perfunctory* testing and no
issues from this change were observed.
This is a necessary first step to some pending plugin/UI work:
* [UI: Alllow for re-ordering third-party plugins' UIs](https://github.com/EDCD/EDMarketConnector/issues/1792)
* [UI: Allow configuration of number of UI columns.](https://github.com/EDCD/EDMarketConnector/issues/1813)
* [Re-work how Plugins' Settings are accessed](https://github.com/EDCD/EDMarketConnector/issues/1814)
* **New** - `capi_fleetcarrier()` function to receive the data from a CAPI
`/fleetcarrier` query. See PLUGINS.md for details.
* It was found that the `ShutDown` event (note the capitalisation, this is
distinct from the actual Journal `Shutdown` event) synthesized for plugins
when it is detected that the game has exited was never actually being
delivered. Instead this was erroneously replaced with a synthesized `StartUp`
event. This has been fixed.
* As the location tracking has been moved out of the core EDDN plugin, and into
monitor.py all of it is now available as members of the `state` dictionary
which is passed to `journal_entry()`.
This both means that no plugin should need to perform such location state
tracking itself *and* they can take advantage of it being fully up to date
when a user restarts the application with the game running.
A reminder: When performing 'catch up' on the newest Journal file found at
startup, the application does **not** pass any events to the
`journal_entry()` method in plugins. This is to avoid spamming with
data/state that has possibly already been handled, and in the case of the
Cmdr moving around will end up not being relevant by the time the end of the
file is reached. This limitation was also why the core EDDN plugin couldn't
properly initiate its location tracking state in this scenario.
See PLUGINS.md for details of the new `state` members. Pay particular
attention to the footnote that details the caveats around Body tracking.
Careful testing has been done for *only* the following. So, if you make use
of any of the other new state values and spot a bug, please report it:
1. SystemName
2. SystemAddress
3. Body (Name)
4. BodyID
5. BodyType
6. StationName
7. StationType
8. (Station) MarketID
* There is an additional property `request_cmdr` on `CAPIData` objects, which
records the name of the Cmdr the request was made for.
* `FDevIDs` files are their latest versions at time of this version's build.
* `examples\plugintest` - dropped the "pre-5.0.0 config" code, as it's long
since irrelevant.
Developers
---
* If you utilise a git clone of the source code, you should also ensure the
sub-modules are initialised and synchronised.
[wiki:Running from source](https://github.com/EDCD/EDMarketConnector/wiki/Running-from-source#obtain-a-copy-of-the-application-source)
has been updated to include the necessary commands.
* The `coriolis-data` git sub-module now uses an HTTPS, not "git" URL, so won't
require authentication for a simple `git pull`.
* If you have a `dump` directory in CWD when running EDMarketConnector.py under
a debugger you will get files in that location when CAPI queries complete.
This will now include files with names of the form
`FleetCarrier.<callsign>.<timstamp>.json` for `/fleetcarrier` data.
* All the main UI tk widgets are now properly named. This might make things
easier if debugging UI widgets as you'll no longer see a bunch of `!label1`,
`!frame1` and the like.
Each plugin's separator is named as per the scheme `plugin_hr_<X>`, and when
a plugin has UI its new container Frame is named `plugin_X`. Both of these
start with `1`, not `0`.
---
Release 5.7.0
===
This release re-enables CAPI queries for Legacy players. As a result, the
+97 -3
View File
@@ -196,6 +196,99 @@ Currently, the only file that defines the version code-wise is
matter handled as part of
[the release process](docs/Releasing.md#distribution).
---
## Python Environment
Whilst you can use whatever IDE/development environment best suits you, much
of the setup in this project has only been tested against PyCharm or VSCode,
along with 'git bash' command-line.
### Use the version denoted by `.python-version`
We only test, and build with, the python version as defined in the file
`.python-version`. Trying to use any other version might mean things just
don't run at all, or don't work as expected.
### Use a Python virtual environment
Always use a Python virtual environment specific to working on this project.
An example, when using Python 3.11.x would be:
```bash
python -m venv ../edmc-venv-3.11
```
Note how the 'venv' is placed in a sub-directory *of the parent directory* of
the project. This avoids any issues with scripts working recursively picking
up your 'venv' files.
If you have good reason to put the 'venv' inside the project directory then
you **MUST** use either `venv` or `.venv`, else you'll run into all sorts of
problems with pre-commit checks.
### Install the development requirements
Whilst simply running the project only requires
`pip install -r requirements.txt`-provided modules, development work will
instead require:
```bash
pip install -r requirements-dev.txt
```
NB: This itself will also take note of `requirements.txt`.
This will ensure you have all the necessary tools to hand for the pre-commit
checks.
### Set up `pre-commit`
In order to have any submitted PR be in the least-worse shape when first opened
you **MUST** run the checks as specified in `.pre-commit-config.yaml`.
```bash
pre-commit install --install-hooks
```
Now whenever you `git commit` the various checks will be run to ensure your
code is compliant with our requirements. If you have a *temporary* need to
bypass this (e.g. wanting to commit one change and fix a non-compliant file
later) you can add `-n` to the `git commit` arguments.
**NB: There is [a problem](https://github.com/microsoft/vscode-python/issues/10165)
with pre-commit if using VSCode.** There's a workaround in
[one of the comments](https://github.com/microsoft/vscode-python/issues/10165#issuecomment-1277237676).
### Consider running `pytest` before any `git push`
The GitHub workflows for PRs and pushes will run `pytest` and flag an error
if the tests don't pass, so it's in your interests to ensure you've not broken
any tests.
You could endeavour to remember to run `pytest` manually, or you could add
this git hook:
`.git/hooks/pre-push`
```bash
#!/bin/sh
# If this script exits with a non-zero status nothing will be pushed.
#
# This hook is called with the following parameters:
#
# $1 -- Name of the remote to which the push is being done
# $2 -- URL to which the push is being done
#
# If pushing without using a named remote those arguments will be equal.
#
# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
# <local ref> <local sha1> <remote ref> <remote sha1>
remote="$1"
url="$2"
echo "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-"
echo " Running pytest..."
pytest || exit 1
echo " All tests passed, proceeding..."
echo "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-"
exit 0
```
It's probably overkill, and will become painful if enough tests are added, to
run `pytest` in a `pre-commit` hook.
---
## Linting
@@ -296,7 +389,7 @@ information about this build process.
Thus, you **MUST** check if any imports you add in `plugins/*.py` files are only
referenced in that file (or also only in any other core plugin), and if so
**YOU MUST ENSURE THAT PERTINENT ADJUSTMENTS ARE MADE IN `setup.py`
**YOU MUST ENSURE THAT PERTINENT ADJUSTMENTS ARE MADE IN `Build-exe-and-msi.py`
IN ORDER TO ENSURE THE FILES ARE ACTUALLY PRESENT IN AN END-USER
INSTALLATION ON WINDOWS.**
@@ -316,8 +409,9 @@ the appropriate `packages` definition to:
Note that in this case it's in `packages` because we want the whole directory
adding. For a single file an extra item in `includes` would suffice.
Such additions to `setup.py` should not cause any issues if subsequent project
changes cause `py2exe` to automatically pick up the same file(s).
Such additions to `Build-exe-and-msi.py` should not cause any issues if
subsequent project changes cause `py2exe` to automatically pick up the same
file(s).
---
+33 -11
View File
@@ -71,7 +71,7 @@ def versioncmp(versionstring) -> List:
return list(map(int, versionstring.split('.')))
def deep_get(target: dict, *args: str, default=None) -> Any:
def deep_get(target: dict | companion.CAPIData, *args: str, default=None) -> Any:
"""
Walk into a dict and return the specified deep value.
@@ -224,12 +224,15 @@ sys.path: {sys.path}'''
logger.debug(f'logdir = "{monitor.currentdir}"')
logfile = monitor.journal_newest_filename(monitor.currentdir)
if logfile is None:
raise ValueError("None from monitor.journal_newest_filename")
logger.debug(f'Using logfile "{logfile}"')
with open(logfile, 'r', encoding='utf-8') as loghandle:
with open(logfile, 'rb', 0) as loghandle:
for line in loghandle:
try:
monitor.parse_entry(line)
except Exception:
logger.debug(f'Invalid journal entry {line!r}')
@@ -323,10 +326,12 @@ sys.path: {sys.path}'''
raise companion.CmdrError()
elif (
data['lastSystem']['name'] != monitor.system or
((data['commander']['docked'] and data['lastStarport']['name'] or None) != monitor.station) or
data['ship']['id'] != monitor.state['ShipID'] or
data['ship']['name'].lower() != monitor.state['ShipType']
data['lastSystem']['name'] != monitor.state['SystemName']
or (
(data['commander']['docked'] and data['lastStarport']['name'] or None) != monitor.state['StationName']
)
or data['ship']['id'] != monitor.state['ShipID']
or data['ship']['name'].lower() != monitor.state['ShipType']
):
raise companion.ServerLagging()
@@ -410,12 +415,29 @@ sys.path: {sys.path}'''
# Retry for shipyard
sleep(SERVER_RETRY)
new_data = companion.session.station()
# might have undocked while we were waiting for retry in which case station data is unreliable
if new_data['commander'].get('docked') and \
deep_get(new_data, 'lastSystem', 'name') == monitor.system and \
deep_get(new_data, 'lastStarport', 'name') == monitor.station:
companion.session.station(int(time()))
# Wait for the response
_capi_request_timeout = 60
try:
capi_response = companion.session.capi_response_queue.get(
block=True, timeout=_capi_request_timeout
)
except queue.Empty:
logger.error(f'CAPI requests timed out after {_capi_request_timeout} seconds')
sys.exit(EXIT_SERVER)
if isinstance(capi_response, companion.EDMCCAPIFailedRequest):
logger.error(f'Failed Request: {capi_response.message}')
sys.exit(EXIT_SERVER)
new_data = capi_response.capi_data
# might have undocked while we were waiting for retry in which case station data is unreliable
if (
new_data['commander'].get('docked')
and deep_get(new_data, 'lastSystem', 'name') == monitor.state['SystemName']
and deep_get(new_data, 'lastStarport', 'name') == monitor.state['StationName']
):
data = new_data
if args.s:
+4 -4
View File
@@ -145,7 +145,7 @@ class Logger:
logging.Logger instance.
"""
def __init__(self, logger_name: str, loglevel: int = _default_loglevel):
def __init__(self, logger_name: str, loglevel: int | str = _default_loglevel):
"""
Set up a `logging.Logger` with our preferred configuration.
@@ -216,7 +216,7 @@ class Logger:
"""
return self.logger_channel
def set_channels_loglevel(self, level: int) -> None:
def set_channels_loglevel(self, level: int | str) -> None:
"""
Set the specified log level on the channels.
@@ -226,7 +226,7 @@ class Logger:
self.logger_channel.setLevel(level)
self.logger_channel_rotating.setLevel(level)
def set_console_loglevel(self, level: int) -> None:
def set_console_loglevel(self, level: int | str) -> None:
"""
Set the specified log level on the console channel.
@@ -541,7 +541,7 @@ def get_main_logger(sublogger_name: str = '') -> 'LoggerMixin':
# Singleton
loglevel = config.get_str('loglevel')
loglevel: str | int = config.get_str('loglevel')
if not loglevel:
loglevel = logging.INFO
+326 -120
View File
@@ -16,7 +16,7 @@ from builtins import object, str
from os import chdir, environ
from os.path import dirname, join
from time import localtime, strftime, time
from typing import TYPE_CHECKING, Literal, Optional, Tuple, Union
from typing import TYPE_CHECKING, Any, Literal, Optional, Tuple, Union
# Have this as early as possible for people running EDMarketConnector.exe
# from cmd.exe or a bat file or similar. Else they might not be in the correct
@@ -168,6 +168,11 @@ if __name__ == '__main__': # noqa: C901
help='Have EDDN plugin show what it is tracking',
action='store_true',
)
parser.add_argument(
'--killswitches-file',
help='Specify a custom killswitches file',
)
###########################################################################
args = parser.parse_args()
@@ -382,7 +387,9 @@ if __name__ == '__main__': # noqa: C901
if (
(
git_branch == 'develop'
or '-alpha0' in str(appversion())
or (
git_branch == '' and '-alpha0' in str(appversion())
)
) and (
(
sys.platform == 'linux'
@@ -469,6 +476,8 @@ class AppWindow(object):
def __init__(self, master: tk.Tk): # noqa: C901, CCR001 # TODO - can possibly factor something out
self.capi_query_holdoff_time = config.get_int('querytime', default=0) + companion.capi_query_cooldown
self.capi_fleetcarrier_query_holdoff_time = config.get_int('fleetcarrierquerytime', default=0) \
+ companion.capi_fleetcarrier_query_cooldown
self.w = master
self.w.title(applongname)
@@ -500,7 +509,7 @@ class AppWindow(object):
else:
self.w.tk.call('wm', 'iconphoto', self.w, '-default',
tk.PhotoImage(file=join(config.respath_path, 'EDMarketConnector.png')))
tk.PhotoImage(file=join(config.respath_path, 'io.edcd.EDMarketConnector.png')))
# TODO: Export to files and merge from them in future ?
self.theme_icon = tk.PhotoImage(
@@ -514,20 +523,21 @@ class AppWindow(object):
frame.grid(sticky=tk.NSEW)
frame.columnconfigure(1, weight=1)
self.cmdr_label = tk.Label(frame)
self.cmdr_label = tk.Label(frame, name='cmdr_label')
self.cmdr = tk.Label(frame, compound=tk.RIGHT, anchor=tk.W, name='cmdr')
self.ship_label = tk.Label(frame)
self.ship_label = tk.Label(frame, name='ship_label')
self.ship = HyperlinkLabel(frame, compound=tk.RIGHT, url=self.shipyard_url, name='ship')
self.suit_label = tk.Label(frame)
self.suit_label = tk.Label(frame, name='suit_label')
self.suit = tk.Label(frame, compound=tk.RIGHT, anchor=tk.W, name='suit')
self.system_label = tk.Label(frame)
self.system_label = tk.Label(frame, name='system_label')
self.system = HyperlinkLabel(frame, compound=tk.RIGHT, url=self.system_url, popup_copy=True, name='system')
self.station_label = tk.Label(frame)
self.station_label = tk.Label(frame, name='station_label')
self.station = HyperlinkLabel(frame, compound=tk.RIGHT, url=self.station_url, name='station')
# system and station text is set/updated by the 'provider' plugins
# eddb, edsm and inara. Look for:
#
# parent.children['system'] / parent.children['station']
# parent.nametowidget(f".{appname.lower()}.system")
# parent.nametowidget(f".{appname.lower()}.station")
ui_row = 1
@@ -551,10 +561,26 @@ class AppWindow(object):
self.station.grid(row=ui_row, column=1, sticky=tk.EW)
ui_row += 1
plugin_no = 0
for plugin in plug.PLUGINS:
appitem = plugin.get_app(frame)
# Per plugin separator
plugin_sep = tk.Frame(
frame, highlightthickness=1, name=f"plugin_hr_{plugin_no + 1}"
)
# Per plugin frame, for it to use as its parent for own widgets
plugin_frame = tk.Frame(
frame,
name=f"plugin_{plugin_no + 1}"
)
appitem = plugin.get_app(plugin_frame)
if appitem:
tk.Frame(frame, highlightthickness=1).grid(columnspan=2, sticky=tk.EW) # separator
plugin_no += 1
plugin_sep.grid(columnspan=2, sticky=tk.EW)
ui_row = frame.grid_size()[1]
plugin_frame.grid(
row=ui_row, columnspan=2, sticky=tk.NSEW
)
plugin_frame.columnconfigure(1, weight=1)
if isinstance(appitem, tuple) and len(appitem) == 2:
ui_row = frame.grid_size()[1]
appitem[0].grid(row=ui_row, column=0, sticky=tk.W)
@@ -563,9 +589,26 @@ class AppWindow(object):
else:
appitem.grid(columnspan=2, sticky=tk.EW)
else:
# This plugin didn't provide any UI, so drop the frames
plugin_frame.destroy()
plugin_sep.destroy()
# LANG: Update button in main window
self.button = ttk.Button(frame, text=_('Update'), width=28, default=tk.ACTIVE, state=tk.DISABLED)
self.theme_button = tk.Label(frame, width=32 if sys.platform == 'darwin' else 28, state=tk.DISABLED)
self.button = ttk.Button(
frame,
name='update_button',
text=_('Update'), # LANG: Main UI Update button
width=28,
default=tk.ACTIVE,
state=tk.DISABLED
)
self.theme_button = tk.Label(
frame,
name='themed_update_button',
width=32 if sys.platform == 'darwin' else 28,
state=tk.DISABLED
)
ui_row = frame.grid_size()[1]
self.button.grid(row=ui_row, columnspan=2, sticky=tk.NSEW)
@@ -583,11 +626,20 @@ class AppWindow(object):
child.grid_configure(padx=self.PADX, pady=(
sys.platform != 'win32' or isinstance(child, tk.Frame)) and 2 or 0)
# The type needs defining for adding the menu entry, but won't be
# properly set until later
self.updater: update.Updater = None
self.menubar = tk.Menu()
# This used to be *after* the menu setup for some reason, but is testing
# as working (both internal and external) like this. -Ath
import update
if getattr(sys, 'frozen', False):
# Running in frozen .exe, so use (Win)Sparkle
self.updater = update.Updater(tkroot=self.w, provider='external')
else:
self.updater = update.Updater(tkroot=self.w, provider='internal')
self.updater.check_for_updates() # Sparkle / WinSparkle does this automatically for packaged apps
if sys.platform == 'darwin':
# Can't handle (de)iconify if topmost is set, so suppress iconify button
# http://wiki.tcl.tk/13428 and p15 of
@@ -613,6 +665,8 @@ class AppWindow(object):
self.menubar.add_cascade(menu=window_menu)
self.help_menu = tk.Menu(self.menubar, name='help')
self.w.createcommand("::tk::mac::ShowHelp", self.help_general)
self.help_menu.add_command(command=self.help_troubleshooting)
self.help_menu.add_command(command=self.help_report_a_bug)
self.help_menu.add_command(command=self.help_privacy)
self.help_menu.add_command(command=self.help_releases)
self.menubar.add_cascade(menu=self.help_menu)
@@ -637,10 +691,13 @@ class AppWindow(object):
self.edit_menu.add_command(accelerator='Ctrl+C', state=tk.DISABLED, command=self.copy)
self.menubar.add_cascade(menu=self.edit_menu)
self.help_menu = tk.Menu(self.menubar, tearoff=tk.FALSE) # type: ignore
self.help_menu.add_command(command=self.help_general)
self.help_menu.add_command(command=self.help_privacy)
self.help_menu.add_command(command=self.help_releases)
self.help_menu.add_command(command=lambda: self.updater.check_for_updates())
self.help_menu.add_command(command=self.help_general) # Documentation
self.help_menu.add_command(command=self.help_troubleshooting) # Troubleshooting
self.help_menu.add_command(command=self.help_report_a_bug) # Report A Bug
self.help_menu.add_command(command=self.help_privacy) # Privacy Policy
self.help_menu.add_command(command=self.help_releases) # Release Notes
self.help_menu.add_command(command=lambda: self.updater.check_for_updates()) # Check for Updates...
# About E:D Market Connector
self.help_menu.add_command(command=lambda: not self.HelpAbout.showing and self.HelpAbout(self.w))
self.menubar.add_cascade(menu=self.help_menu)
@@ -666,11 +723,15 @@ class AppWindow(object):
theme.register(self.help_menu)
# Alternate title bar and menu for dark theme
self.theme_menubar = tk.Frame(frame)
self.theme_menubar = tk.Frame(frame, name="alternate_menubar")
self.theme_menubar.columnconfigure(2, weight=1)
theme_titlebar = tk.Label(self.theme_menubar, text=applongname,
image=self.theme_icon, cursor='fleur',
anchor=tk.W, compound=tk.LEFT)
theme_titlebar = tk.Label(
self.theme_menubar,
name="alternate_titlebar",
text=applongname,
image=self.theme_icon, cursor='fleur',
anchor=tk.W, compound=tk.LEFT
)
theme_titlebar.grid(columnspan=3, padx=2, sticky=tk.NSEW)
self.drag_offset: Tuple[Optional[int], Optional[int]] = (None, None)
theme_titlebar.bind('<Button-1>', self.drag_start)
@@ -703,7 +764,7 @@ class AppWindow(object):
tk.Frame(self.theme_menubar, highlightthickness=1).grid(columnspan=5, padx=self.PADX, sticky=tk.EW)
theme.register(self.theme_minimize) # images aren't automatically registered
theme.register(self.theme_close)
self.blank_menubar = tk.Frame(frame)
self.blank_menubar = tk.Frame(frame, name="blank_menubar")
tk.Label(self.blank_menubar).grid()
tk.Label(self.blank_menubar).grid()
tk.Frame(self.blank_menubar, height=2).grid()
@@ -755,16 +816,6 @@ class AppWindow(object):
# Start a protocol handler to handle cAPI registration. Requires main loop to be running.
self.w.after_idle(lambda: protocol.protocolhandler.start(self.w))
# Load updater after UI creation (for WinSparkle)
import update
if getattr(sys, 'frozen', False):
# Running in frozen .exe, so use (Win)Sparkle
self.updater = update.Updater(tkroot=self.w, provider='external')
else:
self.updater = update.Updater(tkroot=self.w, provider='internal')
self.updater.check_for_updates() # Sparkle / WinSparkle does this automatically for packaged apps
# Migration from <= 3.30
for username in config.get_list('fdev_usernames', default=[]):
config.delete_password(username)
@@ -879,8 +930,11 @@ class AppWindow(object):
self.system_menu.entryconfigure(1, label=_("Check for Updates...")) # LANG: Help > Check for Updates...
self.file_menu.entryconfigure(0, label=_('Save Raw Data...')) # LANG: File > Save Raw Data...
self.view_menu.entryconfigure(0, label=_('Status')) # LANG: File > Status
self.help_menu.entryconfigure(1, label=_('Privacy Policy')) # LANG: Help > Privacy Policy
self.help_menu.entryconfigure(2, label=_('Release Notes')) # LANG: Help > Release Notes
self.help_menu.entryconfigure(1, label=_('Documentation')) # LANG: Help > Documentation
self.help_menu.entryconfigure(2, label=_('Troubleshooting')) # LANG: Help > Troubleshooting
self.help_menu.entryconfigure(3, label=_('Report A Bug')) # LANG: Help > Report A Bug
self.help_menu.entryconfigure(4, label=_('Privacy Policy')) # LANG: Help > Privacy Policy
self.help_menu.entryconfigure(5, label=_('Release Notes')) # LANG: Help > Release Notes
else:
self.menubar.entryconfigure(1, label=_('File')) # LANG: 'File' menu title
self.menubar.entryconfigure(2, label=_('Edit')) # LANG: 'Edit' menu title
@@ -897,16 +951,28 @@ class AppWindow(object):
# Help menu
self.help_menu.entryconfigure(0, label=_('Documentation')) # LANG: Help > Documentation
self.help_menu.entryconfigure(1, label=_('Privacy Policy')) # LANG: Help > Privacy Policy
self.help_menu.entryconfigure(2, label=_('Release Notes')) # LANG: Help > Release Notes
self.help_menu.entryconfigure(3, label=_('Check for Updates...')) # LANG: Help > Check for Updates...
self.help_menu.entryconfigure(4, label=_("About {APP}").format(APP=applongname)) # LANG: Help > About App
self.help_menu.entryconfigure(1, label=_('Troubleshooting')) # LANG: Help > Troubleshooting
self.help_menu.entryconfigure(2, label=_('Report A Bug')) # LANG: Help > Report A Bug
self.help_menu.entryconfigure(3, label=_('Privacy Policy')) # LANG: Help > Privacy Policy
self.help_menu.entryconfigure(4, label=_('Release Notes')) # LANG: Help > Release Notes
self.help_menu.entryconfigure(5, label=_('Check for Updates...')) # LANG: Help > Check for Updates...
self.help_menu.entryconfigure(6, label=_("About {APP}").format(APP=applongname)) # LANG: Help > About App
# Edit menu
self.edit_menu.entryconfigure(0, label=_('Copy')) # LANG: Label for 'Copy' as in 'Copy and Paste'
def login(self):
"""Initiate CAPI/Frontier login and set other necessary state."""
should_return: bool
new_data: dict[str, Any]
should_return, new_data = killswitch.check_killswitch('capi.auth', {})
if should_return:
logger.warning('capi.auth has been disabled via killswitch. Returning.')
# LANG: CAPI auth aborted because of killswitch
self.status['text'] = _('CAPI auth disabled by killswitch')
return
if not self.status['text']:
# LANG: Status - Attempting to get a Frontier Auth Access Token
self.status['text'] = _('Logging in...')
@@ -992,6 +1058,16 @@ class AppWindow(object):
:param event: Tk generated event details.
"""
logger.trace_if('capi.worker', 'Begin')
should_return: bool
new_data: dict[str, Any]
should_return, new_data = killswitch.check_killswitch('capi.auth', {})
if should_return:
logger.warning('capi.auth has been disabled via killswitch. Returning.')
# LANG: CAPI auth query aborted because of killswitch
self.status['text'] = _('CAPI auth disabled by killswitch')
hotkeymgr.play_bad()
return
auto_update = not event
play_sound = (auto_update or int(event.type) == self.EVENT_VIRTUAL) and not config.get_int('hotkey_mute')
@@ -1013,7 +1089,7 @@ class AppWindow(object):
self.status['text'] = _('CAPI query aborted: GameVersion unknown')
return
if not monitor.system:
if not monitor.state['SystemName']:
logger.trace_if('capi.worker', 'Aborting Query: Current star system unknown')
# LANG: CAPI queries aborted because current star system name unknown
self.status['text'] = _('CAPI query aborted: Current system unknown')
@@ -1062,6 +1138,55 @@ class AppWindow(object):
play_sound=play_sound
)
def capi_request_fleetcarrier_data(self, event=None) -> None:
"""
Perform CAPI fleetcarrier data retrieval and associated actions.
This is triggered by certain FleetCarrier journal events
:param event: Tk generated event details.
"""
logger.trace_if('capi.worker', 'Begin')
should_return: bool
new_data: dict[str, Any]
should_return, new_data = killswitch.check_killswitch('capi.request.fleetcarrier', {})
if should_return:
logger.warning('capi.fleetcarrier has been disabled via killswitch. Returning.')
# LANG: CAPI fleetcarrier query aborted because of killswitch
self.status['text'] = _('CAPI fleetcarrier disabled by killswitch')
hotkeymgr.play_bad()
return
if not monitor.cmdr:
logger.trace_if('capi.worker', 'Aborting Query: Cmdr unknown')
# LANG: CAPI fleetcarrier query aborted because Cmdr name is unknown
self.status['text'] = _('CAPI query aborted: Cmdr name unknown')
return
if monitor.state['GameVersion'] is None:
logger.trace_if('capi.worker', 'Aborting Query: GameVersion unknown')
# LANG: CAPI fleetcarrier query aborted because GameVersion unknown
self.status['text'] = _('CAPI query aborted: GameVersion unknown')
return
if not companion.session.retrying:
if time() < self.capi_fleetcarrier_query_holdoff_time: # Was invoked while in cooldown
logger.debug('CAPI fleetcarrier query aborted, too soon since last request')
return
# LANG: Status - Attempting to retrieve data from Frontier CAPI
self.status['text'] = _('Fetching data...')
self.w.update_idletasks()
query_time = int(time())
logger.trace_if('capi.worker', 'Requesting fleetcarrier data')
config.set('fleetcarrierquerytime', query_time)
logger.trace_if('capi.worker', 'Calling companion.session.fleetcarrier')
companion.session.fleetcarrier(
query_time=query_time, tk_response_event=self._CAPI_RESPONSE_TK_EVENT_NAME
)
def capi_handle_response(self, event=None): # noqa: C901, CCR001
"""Handle the resulting data from a CAPI query."""
logger.trace_if('capi.worker', 'Handling response')
@@ -1086,8 +1211,32 @@ class AppWindow(object):
logger.error(msg)
raise ValueError(msg)
if capi_response.capi_data.source_endpoint == companion.session.FRONTIER_CAPI_PATH_FLEETCARRIER:
# Fleetcarrier CAPI response
# Validation
if 'name' not in capi_response.capi_data:
# LANG: No data was returned for the fleetcarrier from the Frontier CAPI
err = self.status['text'] = _('CAPI: No fleetcarrier data returned')
elif not capi_response.capi_data.get('name', {}).get('callsign'):
# LANG: We didn't have the fleetcarrier callsign when we should have
err = self.status['text'] = _("CAPI: Fleetcarrier data incomplete") # Shouldn't happen
else:
if __debug__: # Recording
companion.session.dump_capi_data(capi_response.capi_data)
err = plug.notify_capi_fleetcarrierdata(capi_response.capi_data)
self.status['text'] = err and err or ''
if err:
play_bad = True
self.capi_fleetcarrier_query_holdoff_time = capi_response.query_time \
+ companion.capi_fleetcarrier_query_cooldown
# Other CAPI response
# Validation
if 'commander' not in capi_response.capi_data:
elif 'commander' not in capi_response.capi_data:
# This can happen with EGS Auth if no commander created yet
# LANG: No data was returned for the commander from the Frontier CAPI
err = self.status['text'] = _('CAPI: No commander data returned')
@@ -1123,30 +1272,32 @@ class AppWindow(object):
f"not {capi_response.capi_data['commander'].get('docked')!r}")
raise companion.ServerLagging()
elif capi_response.capi_data['lastSystem']['name'] != monitor.system:
elif capi_response.capi_data['lastSystem']['name'] != monitor.state['SystemName']:
# CAPI system must match last journal one
logger.warning(f"{capi_response.capi_data['lastSystem']['name']!r} != {monitor.system!r}")
logger.warning(f"{capi_response.capi_data['lastSystem']['name']!r} != "
f"{monitor.state['SystemName']!r}")
raise companion.ServerLagging()
elif capi_response.capi_data['lastStarport']['name'] != monitor.station:
if monitor.state['OnFoot'] and monitor.station:
logger.warning(f"({capi_response.capi_data['lastStarport']['name']!r} != {monitor.station!r}) AND "
f"{monitor.state['OnFoot']!r} and {monitor.station!r}")
elif capi_response.capi_data['lastStarport']['name'] != monitor.state['StationName']:
if monitor.state['OnFoot'] and monitor.state['StationName']:
logger.warning(f"({capi_response.capi_data['lastStarport']['name']!r} != "
f"{monitor.state['StationName']!r}) AND "
f"{monitor.state['OnFoot']!r} and {monitor.state['StationName']!r}")
raise companion.ServerLagging()
elif capi_response.capi_data['commander']['docked'] and monitor.station is None:
elif capi_response.capi_data['commander']['docked'] and monitor.state['StationName'] is None:
# Likely (re-)Embarked on ship docked at an EDO settlement.
# Both Disembark and Embark have `"Onstation": false` in Journal.
# So there's nothing to tell us which settlement we're (still,
# or now, if we came here in Apex and then recalled ship) docked at.
logger.debug("docked AND monitor.station is None - so EDO settlement?")
logger.debug("docked AND monitor.state['StationName'] is None - so EDO settlement?")
raise companion.NoMonitorStation()
self.capi_query_holdoff_time = capi_response.query_time + companion.capi_query_cooldown
elif capi_response.capi_data['lastStarport']['id'] != monitor.station_marketid:
elif capi_response.capi_data['lastStarport']['id'] != monitor.state['MarketID']:
logger.warning(f"MarketID mis-match: {capi_response.capi_data['lastStarport']['id']!r} !="
f" {monitor.station_marketid!r}")
f" {monitor.state['MarketID']!r}")
raise companion.ServerLagging()
elif not monitor.state['OnFoot'] and capi_response.capi_data['ship']['id'] != monitor.state['ShipID']:
@@ -1210,10 +1361,18 @@ class AppWindow(object):
if err:
play_bad = True
# Export market data
if not self.export_market_data(capi_response.capi_data):
err = 'Error: Exporting Market data'
play_bad = True
should_return: bool
new_data: dict[str, Any]
should_return, new_data = killswitch.check_killswitch('capi.request./market', {})
if should_return:
logger.warning("capi.request./market has been disabled by killswitch. Returning.")
else:
# Export market data
if not self.export_market_data(capi_response.capi_data):
err = 'Error: Exporting Market data'
play_bad = True
self.capi_query_holdoff_time = capi_response.query_time + companion.capi_query_cooldown
@@ -1228,7 +1387,7 @@ class AppWindow(object):
except companion.CredentialsRequireRefresh:
# We need to 'close' the auth else it'll see STATE_OK and think login() isn't needed
companion.session.close()
companion.session.reinit_session()
# LANG: Frontier CAPI Access Token expired, trying to get a new one
self.status['text'] = _('CAPI: Refreshing access token...')
if companion.session.login():
@@ -1369,7 +1528,7 @@ class AppWindow(object):
self.update_suit_text()
self.suit_show_if_set()
self.edit_menu.entryconfigure(0, state=monitor.system and tk.NORMAL or tk.DISABLED) # Copy
self.edit_menu.entryconfigure(0, state=monitor.state['SystemName'] and tk.NORMAL or tk.DISABLED) # Copy
if entry['event'] in (
'Undocked',
@@ -1396,7 +1555,7 @@ class AppWindow(object):
config.set('cmdrs', config.get_list('cmdrs', default=[]) + [monitor.cmdr])
self.login()
if monitor.mode == 'CQC' and entry['event']:
if monitor.cmdr and monitor.mode == 'CQC' and entry['event']:
err = plug.notify_journal_entry_cqc(monitor.cmdr, monitor.is_beta, entry, monitor.state)
if err:
self.status['text'] = err
@@ -1410,11 +1569,13 @@ class AppWindow(object):
return # Startup
if entry['event'] in ['StartUp', 'LoadGame'] and monitor.started:
logger.info('Startup or LoadGame event')
logger.info('StartUp or LoadGame event')
# Disable WinSparkle automatic update checks, IFF configured to do so when in-game
if config.get_int('disable_autoappupdatecheckingame') and 1:
self.updater.set_automatic_updates_check(False)
if self.updater is not None:
self.updater.set_automatic_updates_check(False)
logger.info('Monitor: Disable WinSparkle automatic update checks')
# Can't start dashboard monitoring
@@ -1426,16 +1587,20 @@ class AppWindow(object):
and config.get_int('output') & config.OUT_SHIP:
monitor.export_ship()
err = plug.notify_journal_entry(monitor.cmdr,
monitor.is_beta,
monitor.system,
monitor.station,
entry,
monitor.state)
if err:
self.status['text'] = err
if not config.get_int('hotkey_mute'):
hotkeymgr.play_bad()
if monitor.cmdr:
err = plug.notify_journal_entry(
monitor.cmdr,
monitor.is_beta,
monitor.state['SystemName'],
monitor.state['StationName'],
entry,
monitor.state
)
if err:
self.status['text'] = err
if not config.get_int('hotkey_mute'):
hotkeymgr.play_bad()
auto_update = False
# Only if auth callback is not pending
@@ -1443,7 +1608,7 @@ class AppWindow(object):
# Only if configured to do so
if (not config.get_int('output') & config.OUT_MKT_MANUAL
and config.get_int('output') & config.OUT_STATION_ANY):
if entry['event'] in ('StartUp', 'Location', 'Docked') and monitor.station:
if entry['event'] in ('StartUp', 'Location', 'Docked') and monitor.state['StationName']:
# TODO: Can you log out in a docked Taxi and then back in to
# the taxi, so 'Location' should be covered here too ?
if entry['event'] == 'Docked' and entry.get('Taxi'):
@@ -1459,13 +1624,25 @@ class AppWindow(object):
elif entry['event'] == 'Disembark' and entry.get('Taxi') and entry.get('OnStation'):
auto_update = True
should_return: bool
new_data: dict[str, Any]
if auto_update:
self.w.after(int(SERVER_RETRY * 1000), self.capi_request_data)
should_return, new_data = killswitch.check_killswitch('capi.auth', {})
if not should_return:
self.w.after(int(SERVER_RETRY * 1000), self.capi_request_data)
if entry['event'] in ('CarrierBuy', 'CarrierStats') and config.get_bool('capi_fleetcarrier'):
should_return, new_data = killswitch.check_killswitch('capi.request.fleetcarrier', {})
if not should_return:
self.w.after(int(SERVER_RETRY * 1000), self.capi_request_fleetcarrier_data)
if entry['event'] == 'ShutDown':
# Enable WinSparkle automatic update checks
# NB: Do this blindly, in case option got changed whilst in-game
self.updater.set_automatic_updates_check(True)
if self.updater is not None:
self.updater.set_automatic_updates_check(True)
logger.info('Monitor: Enable WinSparkle automatic update checks')
def auth(self, event=None) -> None:
@@ -1508,21 +1685,23 @@ class AppWindow(object):
entry = dashboard.status
# Currently we don't do anything with these events
err = plug.notify_dashboard_entry(monitor.cmdr, monitor.is_beta, entry)
if err:
self.status['text'] = err
if not config.get_int('hotkey_mute'):
hotkeymgr.play_bad()
if monitor.cmdr:
err = plug.notify_dashboard_entry(monitor.cmdr, monitor.is_beta, entry)
if err:
self.status['text'] = err
if not config.get_int('hotkey_mute'):
hotkeymgr.play_bad()
def plugin_error(self, event=None) -> None:
"""Display asynchronous error from plugin."""
if plug.last_error.get('msg'):
self.status['text'] = plug.last_error['msg']
if plug.last_error.msg:
self.status['text'] = plug.last_error.msg
self.w.update_idletasks()
if not config.get_int('hotkey_mute'):
hotkeymgr.play_bad()
def shipyard_url(self, shipname: str) -> str:
def shipyard_url(self, shipname: str) -> str | None:
"""Despatch a ship URL to the configured handler."""
if not (loadout := monitor.ship()):
logger.warning('No ship loadout, aborting.')
@@ -1549,13 +1728,18 @@ class AppWindow(object):
return f'file://localhost/{file_name}'
def system_url(self, system: str) -> str:
def system_url(self, system: str) -> str | None:
"""Despatch a system URL to the configured handler."""
return plug.invoke(config.get_str('system_provider'), 'EDSM', 'system_url', monitor.system)
return plug.invoke(
config.get_str('system_provider'), 'EDSM', 'system_url', monitor.state['SystemName']
)
def station_url(self, station: str) -> str:
def station_url(self, station: str) -> str | None:
"""Despatch a station URL to the configured handler."""
return plug.invoke(config.get_str('station_provider'), 'eddb', 'station_url', monitor.system, monitor.station)
return plug.invoke(
config.get_str('station_provider'), 'eddb', 'station_url',
monitor.state['SystemName'], monitor.state['StationName']
)
def cooldown(self) -> None:
"""Display and update the cooldown timer for 'Update' button."""
@@ -1569,28 +1753,43 @@ class AppWindow(object):
else:
self.button['text'] = self.theme_button['text'] = _('Update') # LANG: Update button in main window
self.button['state'] = self.theme_button['state'] = (monitor.cmdr and
monitor.mode and
monitor.mode != 'CQC' and
not monitor.state['Captain'] and
monitor.system and
tk.NORMAL or tk.DISABLED)
self.button['state'] = self.theme_button['state'] = (
monitor.cmdr and
monitor.mode and
monitor.mode != 'CQC' and
not monitor.state['Captain'] and
monitor.state['SystemName'] and
tk.NORMAL or tk.DISABLED
)
def ontop_changed(self, event=None) -> None:
"""Set main window 'on top' state as appropriate."""
config.set('always_ontop', self.always_ontop.get())
self.w.wm_attributes('-topmost', self.always_ontop.get())
if sys.platform == 'win32':
def ontop_changed(self, event=None) -> None:
"""Set main window 'on top' state as appropriate."""
config.set('always_ontop', self.always_ontop.get())
self.w.wm_attributes('-topmost', self.always_ontop.get())
def copy(self, event=None) -> None:
"""Copy system, and possible station, name to clipboard."""
if monitor.system:
if monitor.state['SystemName']:
self.w.clipboard_clear()
self.w.clipboard_append(monitor.station and f'{monitor.system},{monitor.station}' or monitor.system)
self.w.clipboard_append(
f"{monitor.state['SystemName']},{monitor.state['StationName']}" if monitor.state['StationName']
else monitor.state['SystemName']
)
def help_general(self, event=None) -> None:
"""Open Wiki Help page in browser."""
webbrowser.open('https://github.com/EDCD/EDMarketConnector/wiki')
def help_troubleshooting(self, event=None) -> None:
"""Open Wiki Privacy page in browser."""
webbrowser.open("https://github.com/EDCD/EDMarketConnector/wiki/Troubleshooting")
def help_report_a_bug(self, event=None) -> None:
"""Open Wiki Privacy page in browser."""
webbrowser.open("https://github.com/EDCD/EDMarketConnector/issues/new?assignees=&labels=bug%2C+unconfirmed"
"&template=bug_report.md&title=")
def help_privacy(self, event=None) -> None:
"""Open Wiki Privacy page in browser."""
webbrowser.open('https://github.com/EDCD/EDMarketConnector/wiki/Privacy-Policy')
@@ -1630,7 +1829,7 @@ class AppWindow(object):
self.resizable(tk.FALSE, tk.FALSE)
frame = ttk.Frame(self)
frame = tk.Frame(self)
frame.grid(sticky=tk.NSEW)
row = 1
@@ -1643,7 +1842,7 @@ class AppWindow(object):
############################################################
# version <link to changelog>
ttk.Label(frame).grid(row=row, column=0) # spacer
tk.Label(frame).grid(row=row, column=0) # spacer
row += 1
self.appversion_label = tk.Label(frame, text=appversion())
self.appversion_label.grid(row=row, column=0, sticky=tk.E)
@@ -1711,7 +1910,7 @@ class AppWindow(object):
defaultextension=default_extension,
filetypes=[('JSON', '.json'), ('All Files', '*')],
initialdir=config.get_str('outdir'),
initialfile=f'{monitor.system}{monitor.station}.{timestamp}'
initialfile=f"{monitor.state['SystemName']}.{monitor.state['StationName']}.{timestamp}"
)
if not f:
return
@@ -1719,13 +1918,14 @@ class AppWindow(object):
with open(f, 'wb') as h:
h.write(str(companion.session.capi_raw_data).encode(encoding='utf-8'))
def exit_tray(self, systray: 'SysTrayIcon') -> None:
"""Tray icon is shutting down."""
exit_thread = threading.Thread(
target=self.onexit,
daemon=True,
)
exit_thread.start()
if sys.platform == 'win32':
def exit_tray(self, systray: 'SysTrayIcon') -> None:
"""Tray icon is shutting down."""
exit_thread = threading.Thread(
target=self.onexit,
daemon=True,
)
exit_thread.start()
def onexit(self, event=None) -> None:
"""Application shutdown procedure."""
@@ -1751,7 +1951,8 @@ class AppWindow(object):
# First so it doesn't interrupt us
logger.info('Closing update checker...')
self.updater.close()
if self.updater is not None:
self.updater.close()
# Earlier than anything else so plugin code can't interfere *and* it
# won't still be running in a manner that might rely on something
@@ -1859,10 +2060,13 @@ Locale LC_TIME: {locale.getlocale(locale.LC_TIME)}'''
)
def setup_killswitches():
def setup_killswitches(filename: Optional[str]):
"""Download and setup the main killswitch list."""
logger.debug('fetching killswitches...')
killswitch.setup_main_list()
if filename is not None:
filename = "file:" + filename
killswitch.setup_main_list(filename)
def show_killswitch_poppup(root=None):
@@ -1891,17 +2095,15 @@ def show_killswitch_poppup(root=None):
for version in kills:
tk.Label(frame, text=f'Version: {version.version}').grid(row=idx, sticky=tk.W)
idx += 1
for id, reason in version.kills.items():
for id, kill in version.kills.items():
tk.Label(frame, text=id).grid(column=0, row=idx, sticky=tk.W, padx=(10, 0))
tk.Label(frame, text=reason).grid(column=1, row=idx, sticky=tk.E, padx=(0, 10))
tk.Label(frame, text=kill.reason).grid(column=1, row=idx, sticky=tk.E, padx=(0, 10))
idx += 1
idx += 1
ok_button = tk.Button(frame, text="ok", command=tl.destroy)
ok_button = tk.Button(frame, text="Ok", command=tl.destroy)
ok_button.grid(columnspan=2, sticky=tk.EW)
theme.apply(tl)
# Run the app
if __name__ == "__main__": # noqa: C901
@@ -2026,7 +2228,8 @@ sys.path: {sys.path}'''
Translations.install(config.get_str('language')) # Can generate errors so wait til log set up
setup_killswitches()
setup_killswitches(args.killswitches_file)
root = tk.Tk(className=appname.lower())
if sys.platform != 'win32' and ((f := config.get_str('font')) is not None or f != ''):
size = config.get_int('font_size', default=-1)
@@ -2045,10 +2248,13 @@ sys.path: {sys.path}'''
if not ui_scale:
ui_scale = 100
config.set('ui_scale', ui_scale)
theme.default_ui_scale = root.tk.call('tk', 'scaling')
logger.trace_if('tk', f'Default tk scaling = {theme.default_ui_scale}')
theme.startup_ui_scale = ui_scale
root.tk.call('tk', 'scaling', theme.default_ui_scale * float(ui_scale) / 100.0)
if theme.default_ui_scale is not None:
root.tk.call('tk', 'scaling', theme.default_ui_scale * float(ui_scale) / 100.0)
app = AppWindow(root)
def messagebox_not_py3():
+1 -1
Submodule FDevIDs updated: 3fd5d202e5...2aaf3cb023.
+361 -361
View File
@@ -1,623 +1,623 @@
/* Language name */
"!Language" = "Čeština";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:212; eddn.py:584; eddn.py:925; load.py:215; load.py:593; load.py:940; eddn.py:252; eddn.py:699; eddn.py:1439; */
"Sending data to EDDN..." = "Odesílání dat do EDDN...";
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:257; eddn.py:864; eddn.py:898; eddn.py:937; load.py:260; load.py:878; load.py:913; load.py:952; eddn.py:316; eddn.py:1375; eddn.py:1410; eddn.py:1451; */
"Error: Can't connect to EDDN" = "Chyba: Nelze se připojit k EDDN";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:663; load.py:672; eddn.py:1122; */
"Send station data to the Elite Dangerous Data Network" = "Odeslat data o stanici do Elite Dangerous Data Network";
/* eddn.py: Enable EDDN support for system and other scan data checkbox label; In files: eddn.py:673; load.py:682; eddn.py:1133; */
"Send system and scan data to the Elite Dangerous Data Network" = "Odeslat sken a data o systému do Elite Dangerous Data Network";
/* eddn.py: EDDN delay sending until docked option is on, this message notes that a send was skipped due to this; In files: eddn.py:683; load.py:692; eddn.py:1144; */
"Delay sending until docked" = "Pozdržet odeslání po zadokování";
/* edsm.py: Settings>EDSM - Label on checkbox for 'send data'; In files: edsm.py:197; load.py:197; edsm.py:237; */
"Send flight log and Cmdr status to EDSM" = "Odesílat letový log a status CMDRa do EDSM";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:206; load.py:206; edsm.py:247; */
"Elite Dangerous Star Map credentials" = "Přihlašovací údaje Elite Dangerous Star Map";
/* edsm.py: Game Commander name label in EDSM settings; theme.py: Label for commander name in main window; EDMarketConnector.py: Label for commander name in main window; stats.py: Cmdr stats; In files: edsm.py:216; load.py:216; edsm.py:258; theme.py:227; EDMarketConnector.py:822; stats.py:59; */
"Cmdr" = "Cmdr";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:223; load.py:223; edsm.py:266; */
"Commander Name" = "Jméno CMDRa";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:230; inara.py:233; load.py:230; load.py:233; edsm.py:274; inara.py:243; */
"API Key" = "API klíč";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:256; load.py:256; edsm.py:301; prefs.py:520; prefs.py:1174; prefs.py:1207; stats.py:156; stats.py:175; stats.py:194; stats.py:211; */
"None" = "Žádná";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:622; edsm.py:724; load.py:627; load.py:729; edsm.py:747; edsm.py:875; */
"Error: EDSM {MSG}" = "Chyba: EDSM {MSG}";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:658; edsm.py:720; load.py:663; load.py:725; edsm.py:784; edsm.py:870; */
"Error: Can't connect to EDSM" = "Chyba: Nelze se připojit k EDSM";
/* inara.py: Checkbox to enable INARA API Usage; In files: inara.py:215; load.py:215; inara.py:222; */
"Send flight log and Cmdr status to Inara" = "Odesílat letový log a status CMDRa na Inaru";
/* inara.py: Text for INARA API keys link ( goes to https://inara.cz/settings-api ); In files: inara.py:225; load.py:225; inara.py:234; */
"Inara credentials" = "Přihlašovací údaje k Inaře";
/* inara.py: INARA API returned some kind of error (error message will be contained in {MSG}); In files: inara.py:1316; inara.py:1328; load.py:1319; load.py:1331; inara.py:1587; inara.py:1600; */
"Error: Inara {MSG}" = "Chyba: Inara {MSG}";
/* coriolis.py: 'Auto' label for Coriolis site override selection; coriolis.py: Coriolis normal/beta selection - auto; In files: load.py:51; load.py:51; load.py:88; load.py:104; load.py:110; coriolis.py:52; coriolis.py:55; coriolis.py:101; coriolis.py:117; coriolis.py:123; */
"Auto" = "Auto";
/* coriolis.py: 'Normal' label for Coriolis site override selection; coriolis.py: Coriolis normal/beta selection - normal; In files: load.py:51; load.py:88; load.py:102; coriolis.py:53; coriolis.py:99; coriolis.py:115; */
"Normal" = "Normal";
/* coriolis.py: 'Beta' label for Coriolis site override selection; coriolis.py: Coriolis normal/beta selection - beta; In files: load.py:51; load.py:88; load.py:103; coriolis.py:54; coriolis.py:100; coriolis.py:116; */
"Beta" = "Beta";
/* coriolis.py: Settings>Coriolis: Help/hint for changing coriolis URLs; In files: load.py:64:66; coriolis.py:69:71; */
"Set the URL to use with coriolis.io ship loadouts. Note that this MUST end with '/import?data='" = "Nastavte si URL pro konfiguraci lodi na coriolis.io. Adresa MUSÍ končit '/import?data='";
/* coriolis.py: Settings>Coriolis: Label for 'NOT alpha/beta game version' URL; In files: load.py:69; coriolis.py:75; */
"Normal URL" = "Normal URL";
/* coriolis.py: Generic 'Reset' button label; In files: load.py:71; load.py:78; coriolis.py:78; coriolis.py:87; */
"Reset" = "Reset";
/* coriolis.py: Settings>Coriolis: Label for 'alpha/beta game version' URL; In files: load.py:76; coriolis.py:84; */
"Beta URL" = "Beta URL";
/* coriolis.py: Settings>Coriolis - invalid override mode found; In files: load.py:120; coriolis.py:134; */
"Invalid Coriolis override mode!" = "Neplatný režim přepisu Coriolis!";
/* eddb.py: Journal Processing disabled due to an active killswitch; In files: load.py:99; eddb.py:100; */
"EDDB Journal processing disabled. See Log." = "Zpracování EDDB deníku je deaktivováno. Zkontrolujte log.";
/* eddn.py: Killswitch disabled EDDN; In files: load.py:756; eddn.py:1233; */
"EDDN journal handler disabled. See Log." = "Obsluha EDDN deníku je deaktivována. Zkontrolujte log.";
/* edsm.py: EDSM plugin - Journal handling disabled by killswitch; In files: load.py:346; edsm.py:402; */
"EDSM Handler disabled. See Log." = "Obsluha EDSM deaktivována. Zkontrolujte log.";
/* inara.py: INARA support disabled via killswitch; In files: load.py:331; inara.py:341; */
"Inara disabled. See Log." = "Inara deaktivována. Zkontrolujte log.";
/* EDMarketConnector.py: No data was returned for the commander from the Frontier CAPI; In files: test_ast_stuff.py:2; EDMarketConnector.py:1047; */
"CAPI: No commander data returned" = "CAPI: Nebyla vrácena žádná data komandéra";
/* coriolis.py: Settings>Coriolis: Label for selection of using Normal, Beta or 'auto' Coriolis URL; In files: coriolis.py:94; */
"Override Beta/Normal Selection" = "Přepsat volbu Beta/Normal";
/* journal_lock.py: Title text on popup when Journal directory already locked; In files: journal_lock.py:206; */
"Journal directory already locked" = "Adresář deníku je již uzamčen";
/* journal_lock.py: Text for when newly selected Journal directory is already locked; In files: journal_lock.py:223:224; */
"The new Journal Directory location is already locked.{CR}You can either attempt to resolve this and then Retry, or choose to Ignore this." = "Nové umístění adresáře deníku je již uzamčeno. {CR} Můžete zkusit tento problém vyřešit a Opakovat, nebo Ignorovat.";
/* journal_lock.py: Generic 'Retry' button label; In files: journal_lock.py:228; */
"Retry" = "Opakovat";
/* journal_lock.py: Generic 'Ignore' button label; In files: journal_lock.py:232; */
"Ignore" = "Ignorovat";
/* ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; In files: ttkHyperlinkLabel.py:42; EDMarketConnector.py:866; */
"Copy" = "Kopírovat";
/* EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:529; EDMarketConnector.py:828; EDMarketConnector.py:1520; */
"Update" = "Aktualizovat";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:612; prefs.py:867; */
"Always on top" = "Vždy navrchu";
/* EDMarketConnector.py: Unknown suit; In files: EDMarketConnector.py:746; */
"Unknown" = "Neznámý";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:824; EDMarketConnector.py:1279; */
"Role" = "Role";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:824; EDMarketConnector.py:1289; EDMarketConnector.py:1312; stats.py:409; */
"Ship" = "Loď";
/* EDMarketConnector.py: Label for 'Suit' line in main UI; In files: EDMarketConnector.py:825; */
"Suit" = "Oblek";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:826; prefs.py:607; stats.py:411; */
"System" = "Systém";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:827; prefs.py:625; prefs.py:762; stats.py:412; */
"Station" = "Stanice";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:830; EDMarketConnector.py:845; EDMarketConnector.py:848; EDMarketConnector.py:2019; */
"File" = "Soubor";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:831; EDMarketConnector.py:846; EDMarketConnector.py:849; */
"Edit" = "Upravit";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:832; */
"View" = "Zobrazit";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:833; */
"Window" = "Okno";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:834; EDMarketConnector.py:847; EDMarketConnector.py:850; */
"Help" = "Nápověda";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:837; EDMarketConnector.py:863; EDMarketConnector.py:1566; */
"About {APP}" = "O {APP}";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:839; EDMarketConnector.py:862; */
"Check for Updates..." = "Zkontrolovat aktualizace...";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:840; EDMarketConnector.py:854; */
"Save Raw Data..." = "Uložit surová data...";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:841; EDMarketConnector.py:853; stats.py:406; */
"Status" = "Status";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:842; EDMarketConnector.py:860; */
"Privacy Policy" = "Zásady ochrany osobních údajů";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:843; EDMarketConnector.py:861; EDMarketConnector.py:1600; */
"Release Notes" = "Poznámky k verzi";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:855; EDMarketConnector.py:2020; prefs.py:255; */
"Settings" = "Nastavení";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:856; */
"Exit" = "Ukončit";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:859; */
"Documentation" = "Dokumentace";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:872; */
"Logging in..." = "Přihlašování...";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:888; EDMarketConnector.py:1431; */
"Authentication successful" = "Přihlášení bylo úspěšné";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:919; */
"You're not docked at a station!" = "Nejste zadokováni ve stanici!";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:927; */
"Station doesn't have anything!" = "Stanice nenabízí žádné služby!";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:932; */
"Station doesn't have a market!" = "Stanice nemá obchod!";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1006; */
"Fetching data..." = "Načítání dat...";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1051; stats.py:335; */
"Who are you?!" = "Kdo jste?";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1057; stats.py:345; */
"Where are you?!" = "Kde zrovna jste?";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1064; stats.py:353; */
"What are you flying?!" = "S jakou lodí létáte?";
/* EDMarketConnector.py: Frontier CAPI server error when fetching data; In files: EDMarketConnector.py:1176; */
"Frontier CAPI server error" = "Chyba Frontier CAPI serveru";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1226; */
"Last updated at %H:%M:%S" = "Poslední aktualizace %H:%M:%S";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1254; */
"Fighter" = "Fighter";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1255; */
"Gunner" = "Gunner";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1256; */
"Helm" = "Helm";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1514; */
"cooldown {SS}s" = "Počkej ještě {SS}s";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1626; prefs.py:306; */
"OK" = "OK";
/* EDMarketConnector.py: The application is shutting down; In files: EDMarketConnector.py:1693; */
"Shutting down..." = "Vypínání...";
/* EDMarketConnector.py: Popup-text about 'active' plugins without Python 3.x support; In files: EDMarketConnector.py:2008:2014; */
"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." = "Jeden nebo více aktivních pluginů ještě stále nepodporuje Python 3.x. Podívejte se na jejich seznam v záložce '{PLUGINS}' v '{FILE}' > '{SETTINGS}'. Zkontrolujte si jestli není dostupná novější verze pluginu, pokud ne, tak kontaktujte vývojáře pluginu o tom, že musí upravit jejich plugin pro Python 3.x.\n\nPlugin můžete vypnout přidáním '{DISABLED}' na konec názvu jeho složky.";
/* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2018; prefs.py:978; */
"Plugins" = "Pluginy";
/* EDMarketConnector.py: Popup window title for list of 'enabled' plugins that don't work with Python 3.x; In files: EDMarketConnector.py:2029; */
"EDMC: Plugins Without Python 3.x Support" = "EDMC: Pluginy, které nepodporují Python 3.x";
/* companion.py: Frontier CAPI didn't respond; In files: companion.py:218; */
/* companion.py: Frontier CAPI didn't respond; In files: companion.py:226; */
"Error: Frontier CAPI didn't respond" = "Chyba: Frontier CAPI neodpovídá";
/* companion.py: Frontier CAPI data doesn't agree with latest Journal game location; In files: companion.py:237; */
/* companion.py: Frontier CAPI data doesn't agree with latest Journal game location; In files: companion.py:245; */
"Error: Frontier server is lagging" = "Chyba: Server Frontieru laguje";
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:263; */
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:271; */
"Error: Invalid Credentials" = "Chyba: Chybné přihlašovací údaje";
/* companion.py: Frontier CAPI authorisation not for currently game-active commander; In files: companion.py:288; */
/* companion.py: Frontier CAPI authorisation not for currently game-active commander; In files: companion.py:296; */
"Error: Wrong Cmdr" = "Chyba: Nesprávný CMDR";
/* companion.py: Generic error prefix - following text is from Frontier auth service; In files: companion.py:414; companion.py:499; */
/* companion.py: Generic error prefix - following text is from Frontier auth service; In files: companion.py:432; companion.py:517; */
"Error" = "Chyba";
/* companion.py: Frontier auth, no 'usr' section in returned data; companion.py: Frontier auth, no 'customer_id' in 'usr' section in returned data; In files: companion.py:457; companion.py:462; */
/* companion.py: Frontier auth, no 'usr' section in returned data; companion.py: Frontier auth, no 'customer_id' in 'usr' section in returned data; In files: companion.py:475; companion.py:480; */
"Error: Couldn't check token customer_id" = "Chyba: Nejde ověřit token customer_id";
/* companion.py: Frontier auth customer_id doesn't match game session FID; In files: companion.py:468; */
/* companion.py: Frontier auth customer_id doesn't match game session FID; In files: companion.py:486; */
"Error: customer_id doesn't match!" = "Chyba: customer_id nesouhlasí!";
/* companion.py: Failed to get Access Token from Frontier Auth service; In files: companion.py:490; */
/* companion.py: Failed to get Access Token from Frontier Auth service; In files: companion.py:508; */
"Error: unable to get token" = "Chyba: nepovedlo se získat token";
/* companion.py: Frontier CAPI data retrieval failed; In files: companion.py:809; */
/* companion.py: Frontier CAPI data retrieval failed; In files: companion.py:856; */
"Frontier CAPI query failure" = "Dotaz na Frontier CAPI selhal";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:194; prefs.py:468; prefs.py:702; prefs.py:735; */
/* EDMarketConnector.py: Main UI Update button; EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:601; EDMarketConnector.py:919; EDMarketConnector.py:1748; */
"Update" = "Aktualizovat";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:710; prefs.py:875; */
"Always on top" = "Vždy navrchu";
/* EDMarketConnector.py: Unknown suit; In files: EDMarketConnector.py:837; */
"Unknown" = "Neznámý";
/* EDMarketConnector.py: Label for commander name in main window; edsm.py: Game Commander name label in EDSM settings; stats.py: Cmdr stats; theme.py: Label for commander name in main window; In files: EDMarketConnector.py:913; edsm.py:332; stats.py:57; theme.py:290; */
"Cmdr" = "Cmdr";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:915; EDMarketConnector.py:1487; */
"Role" = "Role";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:915; EDMarketConnector.py:1497; EDMarketConnector.py:1520; stats.py:405; */
"Ship" = "Loď";
/* EDMarketConnector.py: Label for 'Suit' line in main UI; In files: EDMarketConnector.py:916; */
"Suit" = "Oblek";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:917; prefs.py:614; stats.py:407; */
"System" = "Systém";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:918; prefs.py:632; prefs.py:770; stats.py:408; */
"Station" = "Stanice";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:921; EDMarketConnector.py:939; EDMarketConnector.py:942; EDMarketConnector.py:2264; */
"File" = "Soubor";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:922; EDMarketConnector.py:940; EDMarketConnector.py:943; */
"Edit" = "Upravit";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:923; */
"View" = "Zobrazit";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:924; */
"Window" = "Okno";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:925; EDMarketConnector.py:941; EDMarketConnector.py:944; */
"Help" = "Nápověda";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:928; EDMarketConnector.py:959; EDMarketConnector.py:1804; */
"About {APP}" = "O {APP}";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:930; EDMarketConnector.py:958; */
"Check for Updates..." = "Zkontrolovat aktualizace...";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:931; EDMarketConnector.py:948; */
"Save Raw Data..." = "Uložit surová data...";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:932; EDMarketConnector.py:947; stats.py:402; */
"Status" = "Status";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:933; EDMarketConnector.py:953; */
"Documentation" = "Dokumentace";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:936; EDMarketConnector.py:956; */
"Privacy Policy" = "Zásady ochrany osobních údajů";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:937; EDMarketConnector.py:957; EDMarketConnector.py:1838; */
"Release Notes" = "Poznámky k verzi";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:949; EDMarketConnector.py:2265; prefs.py:241; */
"Settings" = "Nastavení";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:950; */
"Exit" = "Ukončit";
/* EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; In files: EDMarketConnector.py:962; ttkHyperlinkLabel.py:53; */
"Copy" = "Kopírovat";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:978; */
"Logging in..." = "Přihlašování...";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:994; EDMarketConnector.py:1657; */
"Authentication successful" = "Přihlášení bylo úspěšné";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:1025; */
"You're not docked at a station!" = "Nejste zadokováni ve stanici!";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:1033; */
"Station doesn't have anything!" = "Stanice nenabízí žádné služby!";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:1038; */
"Station doesn't have a market!" = "Stanice nemá obchod!";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1128; EDMarketConnector.py:1179; */
"Fetching data..." = "Načítání dat...";
/* EDMarketConnector.py: No data was returned for the commander from the Frontier CAPI; In files: EDMarketConnector.py:1242; */
"CAPI: No commander data returned" = "CAPI: Nebyla vrácena žádná data komandéra";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1246; stats.py:333; */
"Who are you?!" = "Kdo jste?";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1252; stats.py:341; */
"Where are you?!" = "Kde zrovna jste?";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1259; stats.py:349; */
"What are you flying?!" = "S jakou lodí létáte?";
/* EDMarketConnector.py: Frontier CAPI server error when fetching data; In files: EDMarketConnector.py:1384; */
"Frontier CAPI server error" = "Chyba Frontier CAPI serveru";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1434; */
"Last updated at %H:%M:%S" = "Poslední aktualizace %H:%M:%S";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1462; */
"Fighter" = "Fighter";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1463; */
"Gunner" = "Gunner";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1464; */
"Helm" = "Helm";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1742; */
"cooldown {SS}s" = "Počkej ještě {SS}s";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1864; prefs.py:292; */
"OK" = "OK";
/* EDMarketConnector.py: The application is shutting down; In files: EDMarketConnector.py:1936; */
"Shutting down..." = "Vypínání...";
/* EDMarketConnector.py: Popup-text about 'active' plugins without Python 3.x support; In files: EDMarketConnector.py:2253:2259; */
"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." = "Jeden nebo více aktivních pluginů ještě stále nepodporuje Python 3.x. Podívejte se na jejich seznam v záložce '{PLUGINS}' v '{FILE}' > '{SETTINGS}'. Zkontrolujte si jestli není dostupná novější verze pluginu, pokud ne, tak kontaktujte vývojáře pluginu o tom, že musí upravit jejich plugin pro Python 3.x.\n\nPlugin můžete vypnout přidáním '{DISABLED}' na konec názvu jeho složky.";
/* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2263; prefs.py:986; */
"Plugins" = "Pluginy";
/* EDMarketConnector.py: Popup window title for list of 'enabled' plugins that don't work with Python 3.x; In files: EDMarketConnector.py:2274; */
"EDMC: Plugins Without Python 3.x Support" = "EDMC: Pluginy, které nepodporují Python 3.x";
/* journal_lock.py: Title text on popup when Journal directory already locked; In files: journal_lock.py:208; */
"Journal directory already locked" = "Adresář deníku je již uzamčen";
/* journal_lock.py: Text for when newly selected Journal directory is already locked; In files: journal_lock.py:225:226; */
"The new Journal Directory location is already locked.{CR}You can either attempt to resolve this and then Retry, or choose to Ignore this." = "Nové umístění adresáře deníku je již uzamčeno. {CR} Můžete zkusit tento problém vyřešit a Opakovat, nebo Ignorovat.";
/* journal_lock.py: Generic 'Retry' button label; In files: journal_lock.py:230; */
"Retry" = "Opakovat";
/* journal_lock.py: Generic 'Ignore' button label; In files: journal_lock.py:234; */
"Ignore" = "Ignorovat";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:193; prefs.py:455; prefs.py:709; prefs.py:742; */
"Default" = "Výchozí";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:251; */
/* coriolis.py: 'Auto' label for Coriolis site override selection; coriolis.py: Coriolis normal/beta selection - auto; In files: coriolis.py:74; coriolis.py:77; coriolis.py:123; coriolis.py:139; coriolis.py:145; */
"Auto" = "Auto";
/* coriolis.py: 'Normal' label for Coriolis site override selection; coriolis.py: Coriolis normal/beta selection - normal; In files: coriolis.py:75; coriolis.py:121; coriolis.py:137; */
"Normal" = "Normal";
/* coriolis.py: 'Beta' label for Coriolis site override selection; coriolis.py: Coriolis normal/beta selection - beta; In files: coriolis.py:76; coriolis.py:122; coriolis.py:138; */
"Beta" = "Beta";
/* coriolis.py: Settings>Coriolis: Help/hint for changing coriolis URLs; In files: coriolis.py:91:93; */
"Set the URL to use with coriolis.io ship loadouts. Note that this MUST end with '/import?data='" = "Nastavte si URL pro konfiguraci lodi na coriolis.io. Adresa MUSÍ končit '/import?data='";
/* coriolis.py: Settings>Coriolis: Label for 'NOT alpha/beta game version' URL; In files: coriolis.py:97; */
"Normal URL" = "Normal URL";
/* coriolis.py: Generic 'Reset' button label; In files: coriolis.py:100; coriolis.py:109; */
"Reset" = "Reset";
/* coriolis.py: Settings>Coriolis: Label for 'alpha/beta game version' URL; In files: coriolis.py:106; */
"Beta URL" = "Beta URL";
/* coriolis.py: Settings>Coriolis: Label for selection of using Normal, Beta or 'auto' Coriolis URL; In files: coriolis.py:116; */
"Override Beta/Normal Selection" = "Přepsat volbu Beta/Normal";
/* coriolis.py: Settings>Coriolis - invalid override mode found; In files: coriolis.py:156; */
"Invalid Coriolis override mode!" = "Neplatný režim přepisu Coriolis!";
/* eddb.py: Journal Processing disabled due to an active killswitch; In files: eddb.py:179; */
"EDDB Journal processing disabled. See Log." = "Zpracování EDDB deníku je deaktivováno. Zkontrolujte log.";
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:458; eddn.py:2413; eddn.py:2451; eddn.py:2519; */
"Error: Can't connect to EDDN" = "Chyba: Nelze se připojit k EDDN";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:2041; */
"Send station data to the Elite Dangerous Data Network" = "Odeslat data o stanici do Elite Dangerous Data Network";
/* eddn.py: Enable EDDN support for system and other scan data checkbox label; In files: eddn.py:2052; */
"Send system and scan data to the Elite Dangerous Data Network" = "Odeslat sken a data o systému do Elite Dangerous Data Network";
/* eddn.py: EDDN delay sending until docked option is on, this message notes that a send was skipped due to this; In files: eddn.py:2063; */
"Delay sending until docked" = "Pozdržet odeslání po zadokování";
/* eddn.py: Killswitch disabled EDDN; In files: eddn.py:2178; */
"EDDN journal handler disabled. See Log." = "Obsluha EDDN deníku je deaktivována. Zkontrolujte log.";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:2507; */
"Sending data to EDDN..." = "Odesílání dat do EDDN...";
/* edsm.py: Settings>EDSM - Label on checkbox for 'send data'; In files: edsm.py:308; */
"Send flight log and Cmdr status to EDSM" = "Odesílat letový log a status CMDRa do EDSM";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:319; */
"Elite Dangerous Star Map credentials" = "Přihlašovací údaje Elite Dangerous Star Map";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:341; */
"Commander Name" = "Jméno CMDRa";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:350; inara.py:278; */
"API Key" = "API klíč";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:394; prefs.py:527; prefs.py:1157; prefs.py:1190; stats.py:154; stats.py:173; stats.py:192; stats.py:209; */
"None" = "Žádná";
/* edsm.py: EDSM plugin - Journal handling disabled by killswitch; In files: edsm.py:516; */
"EDSM Handler disabled. See Log." = "Obsluha EDSM deaktivována. Zkontrolujte log.";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:916; edsm.py:1048; */
"Error: EDSM {MSG}" = "Chyba: EDSM {MSG}";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:953; edsm.py:1043; */
"Error: Can't connect to EDSM" = "Chyba: Nelze se připojit k EDSM";
/* inara.py: Checkbox to enable INARA API Usage; In files: inara.py:257; */
"Send flight log and Cmdr status to Inara" = "Odesílat letový log a status CMDRa na Inaru";
/* inara.py: Text for INARA API keys link ( goes to https://inara.cz/settings-api ); In files: inara.py:269; */
"Inara credentials" = "Přihlašovací údaje k Inaře";
/* inara.py: INARA support disabled via killswitch; In files: inara.py:395; */
"Inara disabled. See Log." = "Inara deaktivována. Zkontrolujte log.";
/* inara.py: INARA API returned some kind of error (error message will be contained in {MSG}); In files: inara.py:1650; inara.py:1663; */
"Error: Inara {MSG}" = "Chyba: Inara {MSG}";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:237; */
"Preferences" = "Nastavení";
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:348; */
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:335; */
"Please choose what data to save" = "Vyberte, která data chcete ukládat";
/* prefs.py: Settings > Output option; In files: prefs.py:354; */
/* prefs.py: Settings > Output option; In files: prefs.py:341; */
"Market data in CSV format file" = "Obchodní data ve formátu CSV";
/* prefs.py: Settings > Output option; In files: prefs.py:363; */
/* prefs.py: Settings > Output option; In files: prefs.py:350; */
"Market data in Trade Dangerous format file" = "Obchodní data ve formátu Trade Dangerous";
/* prefs.py: Settings > Output option; In files: prefs.py:373; */
/* prefs.py: Settings > Output option; In files: prefs.py:360; */
"Ship loadout" = "Konfigurace lodi";
/* prefs.py: Settings > Output option; In files: prefs.py:383; */
/* prefs.py: Settings > Output option; In files: prefs.py:370; */
"Automatically update on docking" = "Automaticky aktualizovat při zadokování";
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:392; prefs.py:411; */
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:379; prefs.py:398; */
"File location" = "Cesta k souboru";
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:400; prefs.py:450; */
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:387; prefs.py:437; */
"Change..." = "Změnit...";
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:403; prefs.py:453; */
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:390; prefs.py:440; */
"Browse..." = "Procházet...";
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:418; */
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:405; */
"Output" = "Výstup";
/* prefs.py: Settings > Configuration - Label for Journal files location; In files: prefs.py:444; prefs.py:459; */
/* prefs.py: Settings > Configuration - Label for Journal files location; In files: prefs.py:431; prefs.py:446; */
"E:D journal file location" = "E:D umístění souboru deníku";
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:484; */
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:490; */
"Keyboard shortcut" = "Klávesová zkratka";
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:486; */
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:492; */
"Hotkey" = "Klávesová zkratka";
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:495; */
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:501; */
"Re-start {APP} to use shortcuts" = "Restartujte {APP} pro aktivaci klávesových zkratek";
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:504; */
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:510; */
"{APP} needs permission to use shortcuts" = "{APP} vyžaduje oprávnění pro použití klávesových zkratek";
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:509; */
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:515; */
"Open System Preferences" = "Otevřít Předvolby systému";
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:531; */
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:538; */
"Only when Elite: Dangerous is the active app" = "Pouze pokud je Elite: Dangerous aktivní";
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:542; */
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:549; */
"Play sound" = "Přehrát zvuk";
/* prefs.py: Configuration - disable checks for app updates when in-game; In files: prefs.py:557; */
/* prefs.py: Configuration - disable checks for app updates when in-game; In files: prefs.py:564; */
"Disable Automatic Application Updates Check when in-game" = "Vypnout automatickou kontrolu aktualizací aplikace, pokud jste ve hře";
/* prefs.py: Label for preferred shipyard, system and station 'providers'; In files: prefs.py:570; */
/* prefs.py: Label for preferred shipyard, system and station 'providers'; In files: prefs.py:577; */
"Preferred websites" = "Preferované stránky";
/* prefs.py: Label for Shipyard provider selection; In files: prefs.py:581; */
/* prefs.py: Label for Shipyard provider selection; In files: prefs.py:588; */
"Shipyard" = "Shipyard";
/* prefs.py: Label for checkbox to utilise alternative Coriolis URL method; In files: prefs.py:593; */
/* prefs.py: Label for checkbox to utilise alternative Coriolis URL method; In files: prefs.py:600; */
"Use alternate URL method" = "Použít alternativní URL metodu";
/* prefs.py: Configuration - Label for selection of Log Level; In files: prefs.py:646; */
/* prefs.py: Configuration - Label for selection of Log Level; In files: prefs.py:653; */
"Log Level" = "Míra logování";
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:674; */
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:681; */
"Configuration" = "Nastavení";
/* prefs.py: Preferences privacy tab title; In files: prefs.py:696; */
/* prefs.py: Preferences privacy tab title; In files: prefs.py:703; */
"Privacy" = "Soukromí";
/* prefs.py: Label for Settings > Appeareance > selection of 'normal' text colour; In files: prefs.py:709; */
/* prefs.py: Label for Settings > Appeareance > selection of 'normal' text colour; In files: prefs.py:716; */
"Normal text" = "Normální text";
/* prefs.py: Label for Settings > Appeareance > selection of 'highlightes' text colour; In files: prefs.py:711; */
/* prefs.py: Label for Settings > Appeareance > selection of 'highlightes' text colour; In files: prefs.py:718; */
"Highlighted text" = "Zvýrazněný text";
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:720; */
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:727; */
"Language" = "Jazyk";
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:730; */
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:737; */
"Theme" = "Schéma";
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:741; */
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:749; */
"Dark" = "Tmavé";
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:748; */
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:756; */
"Transparent" = "Průhledné";
/* prefs.py: Appearance - Label for selection of UI scaling; In files: prefs.py:794; */
/* prefs.py: Appearance - Label for selection of UI scaling; In files: prefs.py:802; */
"UI Scale Percentage" = "Procenta UI škálování";
/* prefs.py: Appearance - Help/hint text for UI scaling selection; In files: prefs.py:815; */
/* prefs.py: Appearance - Help/hint text for UI scaling selection; In files: prefs.py:823; */
"100 means Default{CR}Restart Required for{CR}changes to take effect!" = "100 je výchozí hodnota{CR}je potřeba restartovat,{CR}aby se změna projevila!";
/* prefs.py: Appearance - Label for selection of main window transparency; In files: prefs.py:825; */
/* prefs.py: Appearance - Label for selection of main window transparency; In files: prefs.py:833; */
"Main window transparency" = "Průhlednost hlavního okna";
/* prefs.py: Appearance - Help/hint text for Main window transparency selection; In files: prefs.py:845:848; */
/* prefs.py: Appearance - Help/hint text for Main window transparency selection; In files: prefs.py:853:856; */
"100 means fully opaque.{CR}Window is updated in real time" = "100 znamená plně neprůhledné.{CR}Okno se aktualizuje v reálném čase";
/* prefs.py: Appearance option for Windows "minimize to system tray"; In files: prefs.py:877; */
/* prefs.py: Appearance option for Windows "minimize to system tray"; In files: prefs.py:885; */
"Minimize to system tray" = "Minimalizovat do lišty";
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:885; */
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:893; */
"Appearance" = "Vzhled";
/* prefs.py: Label for location of third-party plugins folder; In files: prefs.py:900; */
/* prefs.py: Label for location of third-party plugins folder; In files: prefs.py:908; */
"Plugins folder" = "Složka pluginů";
/* prefs.py: Label on button used to open a filesystem folder; In files: prefs.py:907; */
/* prefs.py: Label on button used to open a filesystem folder; In files: prefs.py:915; */
"Open" = "Otevřít";
/* prefs.py: Tip/label about how to disable plugins; In files: prefs.py:915; */
/* prefs.py: Tip/label about how to disable plugins; In files: prefs.py:923; */
"Tip: You can disable a plugin by{CR}adding '{EXT}' to its folder name" = "Tip: Plugin můžete vypnout přidáním{CR}'{EXT}' do názvu jeho složky";
/* prefs.py: Label on list of enabled plugins; In files: prefs.py:926; */
/* prefs.py: Label on list of enabled plugins; In files: prefs.py:934; */
"Enabled Plugins" = "Zapnuté pluginy";
/* prefs.py: Plugins - Label for list of 'enabled' plugins that don't work with Python 3.x; In files: prefs.py:946; */
/* prefs.py: Plugins - Label for list of 'enabled' plugins that don't work with Python 3.x; In files: prefs.py:954; */
"Plugins Without Python 3.x Support:" = "Pluginy bez podpory Python 3.x:";
/* prefs.py: Plugins - Label on URL to documentation about migrating plugins from Python 2.7; In files: prefs.py:954; */
/* prefs.py: Plugins - Label on URL to documentation about migrating plugins from Python 2.7; In files: prefs.py:962; */
"Information on migrating plugins" = "Informace jak migrovat pluginy.";
/* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:969; */
/* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:977; */
"Disabled Plugins" = "Vypnuté pluginy";
/* stats.py: Cmdr stats; In files: stats.py:60; */
/* stats.py: Cmdr stats; In files: stats.py:58; */
"Balance" = "Zůstatek";
/* stats.py: Cmdr stats; In files: stats.py:61; */
/* stats.py: Cmdr stats; In files: stats.py:59; */
"Loan" = "Půjčka";
/* stats.py: Top rank; In files: stats.py:65; */
/* stats.py: Top rank; In files: stats.py:63; */
"Elite" = "Elite";
/* stats.py: Ranking; In files: stats.py:76; */
/* stats.py: Ranking; In files: stats.py:74; */
"Combat" = "Combat";
/* stats.py: Ranking; In files: stats.py:77; */
/* stats.py: Ranking; In files: stats.py:75; */
"Trade" = "Trade";
/* stats.py: Ranking; In files: stats.py:78; */
/* stats.py: Ranking; In files: stats.py:76; */
"Explorer" = "Explorer";
/* stats.py: Ranking; In files: stats.py:81; */
/* stats.py: Ranking; In files: stats.py:79; */
"CQC" = "CQC";
/* stats.py: Ranking; In files: stats.py:82; */
/* stats.py: Ranking; In files: stats.py:80; */
"Federation" = "Federation";
/* stats.py: Ranking; In files: stats.py:83; */
/* stats.py: Ranking; In files: stats.py:81; */
"Empire" = "Empire";
/* stats.py: Ranking; In files: stats.py:84; */
/* stats.py: Ranking; In files: stats.py:82; */
"Powerplay" = "Powerplay";
/* stats.py: Combat rank; In files: stats.py:93; */
/* stats.py: Combat rank; In files: stats.py:91; */
"Harmless" = "Harmless";
/* stats.py: Combat rank; In files: stats.py:94; */
/* stats.py: Combat rank; In files: stats.py:92; */
"Mostly Harmless" = "Mostly Harmless";
/* stats.py: Combat rank; In files: stats.py:95; */
/* stats.py: Combat rank; In files: stats.py:93; */
"Novice" = "Novice";
/* stats.py: Combat rank; In files: stats.py:96; */
/* stats.py: Combat rank; In files: stats.py:94; */
"Competent" = "Competent";
/* stats.py: Combat rank; In files: stats.py:97; */
/* stats.py: Combat rank; In files: stats.py:95; */
"Expert" = "Expert";
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:98; stats.py:178; */
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:96; stats.py:176; */
"Master" = "Master";
/* stats.py: Combat rank; In files: stats.py:99; */
/* stats.py: Combat rank; In files: stats.py:97; */
"Dangerous" = "Dangerous";
/* stats.py: Combat rank; In files: stats.py:100; */
/* stats.py: Combat rank; In files: stats.py:98; */
"Deadly" = "Deadly";
/* stats.py: Trade rank; In files: stats.py:103; */
/* stats.py: Trade rank; In files: stats.py:101; */
"Penniless" = "Penniless";
/* stats.py: Trade rank; In files: stats.py:104; */
/* stats.py: Trade rank; In files: stats.py:102; */
"Mostly Penniless" = "Mostly Penniless";
/* stats.py: Trade rank; In files: stats.py:105; */
/* stats.py: Trade rank; In files: stats.py:103; */
"Peddler" = "Peddler";
/* stats.py: Trade rank; In files: stats.py:106; */
/* stats.py: Trade rank; In files: stats.py:104; */
"Dealer" = "Dealer";
/* stats.py: Trade rank; In files: stats.py:107; */
/* stats.py: Trade rank; In files: stats.py:105; */
"Merchant" = "Merchant";
/* stats.py: Trade rank; In files: stats.py:108; */
/* stats.py: Trade rank; In files: stats.py:106; */
"Broker" = "Broker";
/* stats.py: Trade rank; In files: stats.py:109; */
/* stats.py: Trade rank; In files: stats.py:107; */
"Entrepreneur" = "Entrepreneur";
/* stats.py: Trade rank; In files: stats.py:110; */
/* stats.py: Trade rank; In files: stats.py:108; */
"Tycoon" = "Tycoon";
/* stats.py: Explorer rank; In files: stats.py:113; */
/* stats.py: Explorer rank; In files: stats.py:111; */
"Aimless" = "Aimless";
/* stats.py: Explorer rank; In files: stats.py:114; */
/* stats.py: Explorer rank; In files: stats.py:112; */
"Mostly Aimless" = "Mostly Aimless";
/* stats.py: Explorer rank; In files: stats.py:115; */
/* stats.py: Explorer rank; In files: stats.py:113; */
"Scout" = "Scout";
/* stats.py: Explorer rank; In files: stats.py:116; */
/* stats.py: Explorer rank; In files: stats.py:114; */
"Surveyor" = "Surveyor";
/* stats.py: Explorer rank; In files: stats.py:117; */
/* stats.py: Explorer rank; In files: stats.py:115; */
"Trailblazer" = "Trailblazer";
/* stats.py: Explorer rank; In files: stats.py:118; */
/* stats.py: Explorer rank; In files: stats.py:116; */
"Pathfinder" = "Pathfinder";
/* stats.py: Explorer rank; In files: stats.py:119; */
/* stats.py: Explorer rank; In files: stats.py:117; */
"Ranger" = "Ranger";
/* stats.py: Explorer rank; In files: stats.py:120; */
/* stats.py: Explorer rank; In files: stats.py:118; */
"Pioneer" = "Pioneer";
/* stats.py: CQC rank; In files: stats.py:144; */
/* stats.py: CQC rank; In files: stats.py:142; */
"Helpless" = "Helpless";
/* stats.py: CQC rank; In files: stats.py:145; */
/* stats.py: CQC rank; In files: stats.py:143; */
"Mostly Helpless" = "Mostly Helpless";
/* stats.py: CQC rank; In files: stats.py:146; */
/* stats.py: CQC rank; In files: stats.py:144; */
"Amateur" = "Amateur";
/* stats.py: CQC rank; In files: stats.py:147; */
/* stats.py: CQC rank; In files: stats.py:145; */
"Semi Professional" = "Semi Professional";
/* stats.py: CQC rank; In files: stats.py:148; */
/* stats.py: CQC rank; In files: stats.py:146; */
"Professional" = "Professional";
/* stats.py: CQC rank; In files: stats.py:149; */
/* stats.py: CQC rank; In files: stats.py:147; */
"Champion" = "Champion";
/* stats.py: CQC rank; In files: stats.py:150; */
/* stats.py: CQC rank; In files: stats.py:148; */
"Hero" = "Hero";
/* stats.py: CQC rank; In files: stats.py:151; */
/* stats.py: CQC rank; In files: stats.py:149; */
"Gladiator" = "Gladiator";
/* stats.py: Federation rank; In files: stats.py:157; */
/* stats.py: Federation rank; In files: stats.py:155; */
"Recruit" = "Recruit";
/* stats.py: Federation rank; In files: stats.py:158; */
/* stats.py: Federation rank; In files: stats.py:156; */
"Cadet" = "Cadet";
/* stats.py: Federation rank; In files: stats.py:159; */
/* stats.py: Federation rank; In files: stats.py:157; */
"Midshipman" = "Midshipman";
/* stats.py: Federation rank; In files: stats.py:160; */
/* stats.py: Federation rank; In files: stats.py:158; */
"Petty Officer" = "Poddůstojník";
/* stats.py: Federation rank; In files: stats.py:161; */
/* stats.py: Federation rank; In files: stats.py:159; */
"Chief Petty Officer" = "Chief Petty Officer";
/* stats.py: Federation rank; In files: stats.py:162; */
/* stats.py: Federation rank; In files: stats.py:160; */
"Warrant Officer" = "Warrant Officer";
/* stats.py: Federation rank; In files: stats.py:163; */
/* stats.py: Federation rank; In files: stats.py:161; */
"Ensign" = "Ensign";
/* stats.py: Federation rank; In files: stats.py:164; */
/* stats.py: Federation rank; In files: stats.py:162; */
"Lieutenant" = "Lieutenant";
/* stats.py: Federation rank; In files: stats.py:165; */
/* stats.py: Federation rank; In files: stats.py:163; */
"Lieutenant Commander" = "Lieutenant Commander";
/* stats.py: Federation rank; In files: stats.py:166; */
/* stats.py: Federation rank; In files: stats.py:164; */
"Post Commander" = "Post Commander";
/* stats.py: Federation rank; In files: stats.py:167; */
/* stats.py: Federation rank; In files: stats.py:165; */
"Post Captain" = "Post Captain";
/* stats.py: Federation rank; In files: stats.py:168; */
/* stats.py: Federation rank; In files: stats.py:166; */
"Rear Admiral" = "Rear Admiral";
/* stats.py: Federation rank; In files: stats.py:169; */
/* stats.py: Federation rank; In files: stats.py:167; */
"Vice Admiral" = "Vice Admiral";
/* stats.py: Federation rank; In files: stats.py:170; */
/* stats.py: Federation rank; In files: stats.py:168; */
"Admiral" = "Admiral";
/* stats.py: Empire rank; In files: stats.py:176; */
/* stats.py: Empire rank; In files: stats.py:174; */
"Outsider" = "Outsider";
/* stats.py: Empire rank; In files: stats.py:177; */
/* stats.py: Empire rank; In files: stats.py:175; */
"Serf" = "Serf";
/* stats.py: Empire rank; In files: stats.py:179; */
/* stats.py: Empire rank; In files: stats.py:177; */
"Squire" = "Squire";
/* stats.py: Empire rank; In files: stats.py:180; */
/* stats.py: Empire rank; In files: stats.py:178; */
"Knight" = "Knight";
/* stats.py: Empire rank; In files: stats.py:181; */
/* stats.py: Empire rank; In files: stats.py:179; */
"Lord" = "Lord";
/* stats.py: Empire rank; In files: stats.py:182; */
/* stats.py: Empire rank; In files: stats.py:180; */
"Baron" = "Baron";
/* stats.py: Empire rank; In files: stats.py:183; */
/* stats.py: Empire rank; In files: stats.py:181; */
"Viscount" = "Viscount";
/* stats.py: Empire rank; In files: stats.py:184; */
/* stats.py: Empire rank; In files: stats.py:182; */
"Count" = "Count";
/* stats.py: Empire rank; In files: stats.py:185; */
/* stats.py: Empire rank; In files: stats.py:183; */
"Earl" = "Earl";
/* stats.py: Empire rank; In files: stats.py:186; */
/* stats.py: Empire rank; In files: stats.py:184; */
"Marquis" = "Marquis";
/* stats.py: Empire rank; In files: stats.py:187; */
/* stats.py: Empire rank; In files: stats.py:185; */
"Duke" = "Duke";
/* stats.py: Empire rank; In files: stats.py:188; */
/* stats.py: Empire rank; In files: stats.py:186; */
"Prince" = "Prince";
/* stats.py: Empire rank; In files: stats.py:189; */
/* stats.py: Empire rank; In files: stats.py:187; */
"King" = "King";
/* stats.py: Power rank; In files: stats.py:195; */
/* stats.py: Power rank; In files: stats.py:193; */
"Rating 1" = "Rating 1";
/* stats.py: Power rank; In files: stats.py:196; */
/* stats.py: Power rank; In files: stats.py:194; */
"Rating 2" = "Rating 2";
/* stats.py: Power rank; In files: stats.py:197; */
/* stats.py: Power rank; In files: stats.py:195; */
"Rating 3" = "Rating 3";
/* stats.py: Power rank; In files: stats.py:198; */
/* stats.py: Power rank; In files: stats.py:196; */
"Rating 4" = "Rating 4";
/* stats.py: Power rank; In files: stats.py:199; */
/* stats.py: Power rank; In files: stats.py:197; */
"Rating 5" = "Rating 5";
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:413; */
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:409; */
"Value" = "Cena";
/* stats.py: Status dialog title; In files: stats.py:422; */
/* stats.py: Status dialog title; In files: stats.py:418; */
"Ships" = "Lodě";
+447 -417
View File
File diff suppressed because it is too large. Load diff
+453 -432
View File
File diff suppressed because it is too large. Load diff
+374 -374
View File
File diff suppressed because it is too large. Load diff
+334 -334
View File
@@ -1,596 +1,596 @@
/* Language name */
"!Language" = "Suomi";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:212; eddn.py:584; eddn.py:925; load.py:215; load.py:593; load.py:940; eddn.py:252; eddn.py:699; eddn.py:1439; */
"Sending data to EDDN..." = "Lähetetään tietoja EDDN:ään...";
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:257; eddn.py:864; eddn.py:898; eddn.py:937; load.py:260; load.py:878; load.py:913; load.py:952; eddn.py:316; eddn.py:1375; eddn.py:1410; eddn.py:1451; */
"Error: Can't connect to EDDN" = "Virhe: ei saatu yhteyttä EDDN:ään";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:663; load.py:672; eddn.py:1122; */
"Send station data to the Elite Dangerous Data Network" = "Lähetä asematiedot Elite Dangerous Data Network -palveluun";
/* eddn.py: Enable EDDN support for system and other scan data checkbox label; In files: eddn.py:673; load.py:682; eddn.py:1133; */
"Send system and scan data to the Elite Dangerous Data Network" = "Lähetä järjestelmä- ja skannaustiedot Elite Dangerous Data Network -palveluun";
/* eddn.py: EDDN delay sending until docked option is on, this message notes that a send was skipped due to this; In files: eddn.py:683; load.py:692; eddn.py:1144; */
"Delay sending until docked" = "Lähetä tiedot vasta telakoitumisen jälkeen";
/* edsm.py: Settings>EDSM - Label on checkbox for 'send data'; In files: edsm.py:197; load.py:197; edsm.py:237; */
"Send flight log and Cmdr status to EDSM" = "Lähetä lentoloki ja komentajatiedot EDSM:ään";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:206; load.py:206; edsm.py:247; */
"Elite Dangerous Star Map credentials" = "Elite Dangerous Star Map -kirjautuminen";
/* edsm.py: Game Commander name label in EDSM settings; theme.py: Label for commander name in main window; EDMarketConnector.py: Label for commander name in main window; stats.py: Cmdr stats; In files: edsm.py:216; load.py:216; edsm.py:258; theme.py:227; EDMarketConnector.py:822; stats.py:59; */
"Cmdr" = "Cmdr";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:223; load.py:223; edsm.py:266; */
"Commander Name" = "Komentajanimi";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:230; inara.py:233; load.py:230; load.py:233; edsm.py:274; inara.py:243; */
"API Key" = "API avain";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:256; load.py:256; edsm.py:301; prefs.py:520; prefs.py:1174; prefs.py:1207; stats.py:156; stats.py:175; stats.py:194; stats.py:211; */
"None" = "Ei määritelty";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:622; edsm.py:724; load.py:627; load.py:729; edsm.py:747; edsm.py:875; */
"Error: EDSM {MSG}" = "Virhe: EDSM {MSG}";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:658; edsm.py:720; load.py:663; load.py:725; edsm.py:784; edsm.py:870; */
"Error: Can't connect to EDSM" = "Virhe: ei saatu yhteyttä EDSM:ään";
/* inara.py: Checkbox to enable INARA API Usage; In files: inara.py:215; load.py:215; inara.py:222; */
"Send flight log and Cmdr status to Inara" = "Lähetä lentoloki ja komentajatiedot Inaraan";
/* inara.py: Text for INARA API keys link ( goes to https://inara.cz/settings-api ); In files: inara.py:225; load.py:225; inara.py:234; */
"Inara credentials" = "Inaran kirjautuminen";
/* inara.py: INARA API returned some kind of error (error message will be contained in {MSG}); In files: inara.py:1316; inara.py:1328; load.py:1319; load.py:1331; inara.py:1587; inara.py:1600; */
"Error: Inara {MSG}" = "Inaran virhe: {MSG}";
/* eddb.py: Journal Processing disabled due to an active killswitch; In files: load.py:99; eddb.py:100; */
"EDDB Journal processing disabled. See Log." = "EDDB lokikirjan käsittely ei käytössä. Katso lokitiedot.";
/* eddn.py: Killswitch disabled EDDN; In files: load.py:756; eddn.py:1233; */
"EDDN journal handler disabled. See Log." = "EDDB lokikirjan käsittelijä ei käytössä. Katso lokitiedot.";
/* edsm.py: EDSM plugin - Journal handling disabled by killswitch; In files: load.py:346; edsm.py:402; */
"EDSM Handler disabled. See Log." = "ESDM käsittelijä ei käytössä. Katso lokitiedot.";
/* inara.py: INARA support disabled via killswitch; In files: load.py:331; inara.py:341; */
"Inara disabled. See Log." = "Inara ei käytössä. Katso lokitiedot.";
/* EDMarketConnector.py: No data was returned for the commander from the Frontier CAPI; In files: test_ast_stuff.py:2; EDMarketConnector.py:1047; */
"CAPI: No commander data returned" = "CAPI: tietoja ei löytynyt";
/* journal_lock.py: Title text on popup when Journal directory already locked; In files: journal_lock.py:206; */
"Journal directory already locked" = "Journal -hakemisto on jo lukittu";
/* journal_lock.py: Text for when newly selected Journal directory is already locked; In files: journal_lock.py:223:224; */
"The new Journal Directory location is already locked.{CR}You can either attempt to resolve this and then Retry, or choose to Ignore this." = "Uusi Journal -hakemisto on jo lukittu.{CR}Voit joko yrittää ratkaista tämän ja yrittää uudelleen, tai jättää asian huomiotta.";
/* journal_lock.py: Generic 'Retry' button label; In files: journal_lock.py:228; */
"Retry" = "Yritä uudelleen";
/* journal_lock.py: Generic 'Ignore' button label; In files: journal_lock.py:232; */
"Ignore" = "Älä huomioi";
/* ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; In files: ttkHyperlinkLabel.py:42; EDMarketConnector.py:866; */
"Copy" = "Kopioi";
/* EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:529; EDMarketConnector.py:828; EDMarketConnector.py:1520; */
"Update" = "Päivitä";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:612; prefs.py:867; */
"Always on top" = "Aina päällimmäisenä";
/* EDMarketConnector.py: Unknown suit; In files: EDMarketConnector.py:746; */
"Unknown" = "Tuntematon";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:824; EDMarketConnector.py:1279; */
"Role" = "Rooli";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:824; EDMarketConnector.py:1289; EDMarketConnector.py:1312; stats.py:409; */
"Ship" = "Alus";
/* EDMarketConnector.py: Label for 'Suit' line in main UI; In files: EDMarketConnector.py:825; */
"Suit" = "Asuste";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:826; prefs.py:607; stats.py:411; */
"System" = "Järjestelmä";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:827; prefs.py:625; prefs.py:762; stats.py:412; */
"Station" = "Asema";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:830; EDMarketConnector.py:845; EDMarketConnector.py:848; EDMarketConnector.py:2019; */
"File" = "Tiedosto";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:831; EDMarketConnector.py:846; EDMarketConnector.py:849; */
"Edit" = "Muokkaa";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:832; */
"View" = "Näytä";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:833; */
"Window" = "Ikkuna";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:834; EDMarketConnector.py:847; EDMarketConnector.py:850; */
"Help" = "Apua";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:837; EDMarketConnector.py:863; EDMarketConnector.py:1566; */
"About {APP}" = "Ohjelmasta {APP}";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:839; EDMarketConnector.py:862; */
"Check for Updates..." = "Tarkista päivitykset...";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:840; EDMarketConnector.py:854; */
"Save Raw Data..." = "Tallenna raakadata...";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:841; EDMarketConnector.py:853; stats.py:406; */
"Status" = "Tila";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:842; EDMarketConnector.py:860; */
"Privacy Policy" = "Tietosuojakäytäntö";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:843; EDMarketConnector.py:861; EDMarketConnector.py:1600; */
"Release Notes" = "Julkaisutiedot";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:855; EDMarketConnector.py:2020; prefs.py:255; */
"Settings" = "Valinnat";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:856; */
"Exit" = "Lopeta";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:859; */
"Documentation" = "Dokumentaatio";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:872; */
"Logging in..." = "Kirjaudutaan...";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:888; EDMarketConnector.py:1431; */
"Authentication successful" = "Todennus onnistui";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:919; */
"You're not docked at a station!" = "Et ole telakoituneena asemalle!";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:927; */
"Station doesn't have anything!" = "Asemalla ei ole mitään!";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:932; */
"Station doesn't have a market!" = "Asemalla ei ole kaupankäyntiä!";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1006; */
"Fetching data..." = "Noudetaan tietoja...";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1051; stats.py:335; */
"Who are you?!" = "Kuka olet?!";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1057; stats.py:345; */
"Where are you?!" = "Missä olet?!";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1064; stats.py:353; */
"What are you flying?!" = "Mitä alusta lennät?!";
/* EDMarketConnector.py: Frontier CAPI server error when fetching data; In files: EDMarketConnector.py:1176; */
"Frontier CAPI server error" = "Frontier CAPI palvelinvirhe.";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1226; */
"Last updated at %H:%M:%S" = "Päivitetty viimeksi %H:%M:%S";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1254; */
"Fighter" = "Hävittäjä";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1255; */
"Gunner" = "Aseiden käyttäjä";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1256; */
"Helm" = "Pilotti";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1514; */
"cooldown {SS}s" = "Jäähtymisaika {SS}s";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1626; prefs.py:306; */
"OK" = "OK";
/* EDMarketConnector.py: The application is shutting down; In files: EDMarketConnector.py:1693; */
"Shutting down..." = "Sulkeutuu...";
/* EDMarketConnector.py: Popup-text about 'active' plugins without Python 3.x support; In files: EDMarketConnector.py:2008:2014; */
"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." = "Yksi tai useampi käyttämistäsi lisäosista ei sisällä vielä Pyhton 3.x -tukea. Tarkista tiedoston '{FILE}' > '{SETTINGS}' välilehti '{PLUGINS}' lisäosista, onko niistä uudempaa versiota saatavilla; muussa tapauksessa ota yhteyttä sen kehittäjään ja kerro, että sen lähdekoodi on päivitettävä Python 3.x -tasolle.\n\nVoit estää lisäosan toiminnan nimeämällä sen hakemiston siten, että se päättyy tekstiin '{DISABLED}'.";
/* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2018; prefs.py:978; */
"Plugins" = "Lisäosat";
/* EDMarketConnector.py: Popup window title for list of 'enabled' plugins that don't work with Python 3.x; In files: EDMarketConnector.py:2029; */
"EDMC: Plugins Without Python 3.x Support" = "EDMC: Lisäosat ilman Python 3.x -tukea";
/* companion.py: Frontier CAPI didn't respond; In files: companion.py:218; */
/* companion.py: Frontier CAPI didn't respond; In files: companion.py:226; */
"Error: Frontier CAPI didn't respond" = "Virhe: Frontier CAPI ei vastaa.";
/* companion.py: Frontier CAPI data doesn't agree with latest Journal game location; In files: companion.py:237; */
/* companion.py: Frontier CAPI data doesn't agree with latest Journal game location; In files: companion.py:245; */
"Error: Frontier server is lagging" = "Virhe: Frontierin palvelin vastaa vanhoilla tiedoilla";
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:263; */
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:271; */
"Error: Invalid Credentials" = "Virhe: kirjautuminen ei kelpaa";
/* companion.py: Frontier CAPI authorisation not for currently game-active commander; In files: companion.py:288; */
/* companion.py: Frontier CAPI authorisation not for currently game-active commander; In files: companion.py:296; */
"Error: Wrong Cmdr" = "Virhe: väärä komentaja";
/* companion.py: Generic error prefix - following text is from Frontier auth service; In files: companion.py:414; companion.py:499; */
/* companion.py: Generic error prefix - following text is from Frontier auth service; In files: companion.py:432; companion.py:517; */
"Error" = "Virhe";
/* companion.py: Frontier auth, no 'usr' section in returned data; companion.py: Frontier auth, no 'customer_id' in 'usr' section in returned data; In files: companion.py:457; companion.py:462; */
/* companion.py: Frontier auth, no 'usr' section in returned data; companion.py: Frontier auth, no 'customer_id' in 'usr' section in returned data; In files: companion.py:475; companion.py:480; */
"Error: Couldn't check token customer_id" = "Virhe: Ei kyetty tarkastamaan polettia customer_id";
/* companion.py: Frontier auth customer_id doesn't match game session FID; In files: companion.py:468; */
/* companion.py: Frontier auth customer_id doesn't match game session FID; In files: companion.py:486; */
"Error: customer_id doesn't match!" = "Virhe: customer_id ei löydy vastaavuutta!";
/* companion.py: Failed to get Access Token from Frontier Auth service; In files: companion.py:490; */
/* companion.py: Failed to get Access Token from Frontier Auth service; In files: companion.py:508; */
"Error: unable to get token" = "Virhe: ei kyetty saamaan polettia";
/* companion.py: Frontier CAPI data retrieval failed; In files: companion.py:809; */
/* companion.py: Frontier CAPI data retrieval failed; In files: companion.py:856; */
"Frontier CAPI query failure" = "Frontier CAPI kyselyvirhe.";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:194; prefs.py:468; prefs.py:702; prefs.py:735; */
/* EDMarketConnector.py: Main UI Update button; EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:601; EDMarketConnector.py:919; EDMarketConnector.py:1748; */
"Update" = "Päivitä";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:710; prefs.py:875; */
"Always on top" = "Aina päällimmäisenä";
/* EDMarketConnector.py: Unknown suit; In files: EDMarketConnector.py:837; */
"Unknown" = "Tuntematon";
/* EDMarketConnector.py: Label for commander name in main window; edsm.py: Game Commander name label in EDSM settings; stats.py: Cmdr stats; theme.py: Label for commander name in main window; In files: EDMarketConnector.py:913; edsm.py:332; stats.py:57; theme.py:290; */
"Cmdr" = "Cmdr";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:915; EDMarketConnector.py:1487; */
"Role" = "Rooli";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:915; EDMarketConnector.py:1497; EDMarketConnector.py:1520; stats.py:405; */
"Ship" = "Alus";
/* EDMarketConnector.py: Label for 'Suit' line in main UI; In files: EDMarketConnector.py:916; */
"Suit" = "Asuste";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:917; prefs.py:614; stats.py:407; */
"System" = "Järjestelmä";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:918; prefs.py:632; prefs.py:770; stats.py:408; */
"Station" = "Asema";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:921; EDMarketConnector.py:939; EDMarketConnector.py:942; EDMarketConnector.py:2264; */
"File" = "Tiedosto";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:922; EDMarketConnector.py:940; EDMarketConnector.py:943; */
"Edit" = "Muokkaa";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:923; */
"View" = "Näytä";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:924; */
"Window" = "Ikkuna";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:925; EDMarketConnector.py:941; EDMarketConnector.py:944; */
"Help" = "Apua";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:928; EDMarketConnector.py:959; EDMarketConnector.py:1804; */
"About {APP}" = "Ohjelmasta {APP}";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:930; EDMarketConnector.py:958; */
"Check for Updates..." = "Tarkista päivitykset...";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:931; EDMarketConnector.py:948; */
"Save Raw Data..." = "Tallenna raakadata...";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:932; EDMarketConnector.py:947; stats.py:402; */
"Status" = "Tila";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:933; EDMarketConnector.py:953; */
"Documentation" = "Dokumentaatio";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:936; EDMarketConnector.py:956; */
"Privacy Policy" = "Tietosuojakäytäntö";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:937; EDMarketConnector.py:957; EDMarketConnector.py:1838; */
"Release Notes" = "Julkaisutiedot";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:949; EDMarketConnector.py:2265; prefs.py:241; */
"Settings" = "Valinnat";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:950; */
"Exit" = "Lopeta";
/* EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; In files: EDMarketConnector.py:962; ttkHyperlinkLabel.py:53; */
"Copy" = "Kopioi";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:978; */
"Logging in..." = "Kirjaudutaan...";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:994; EDMarketConnector.py:1657; */
"Authentication successful" = "Todennus onnistui";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:1025; */
"You're not docked at a station!" = "Et ole telakoituneena asemalle!";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:1033; */
"Station doesn't have anything!" = "Asemalla ei ole mitään!";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:1038; */
"Station doesn't have a market!" = "Asemalla ei ole kaupankäyntiä!";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1128; EDMarketConnector.py:1179; */
"Fetching data..." = "Noudetaan tietoja...";
/* EDMarketConnector.py: No data was returned for the commander from the Frontier CAPI; In files: EDMarketConnector.py:1242; */
"CAPI: No commander data returned" = "CAPI: tietoja ei löytynyt";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1246; stats.py:333; */
"Who are you?!" = "Kuka olet?!";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1252; stats.py:341; */
"Where are you?!" = "Missä olet?!";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1259; stats.py:349; */
"What are you flying?!" = "Mitä alusta lennät?!";
/* EDMarketConnector.py: Frontier CAPI server error when fetching data; In files: EDMarketConnector.py:1384; */
"Frontier CAPI server error" = "Frontier CAPI palvelinvirhe.";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1434; */
"Last updated at %H:%M:%S" = "Päivitetty viimeksi %H:%M:%S";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1462; */
"Fighter" = "Hävittäjä";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1463; */
"Gunner" = "Aseiden käyttäjä";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1464; */
"Helm" = "Pilotti";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1742; */
"cooldown {SS}s" = "Jäähtymisaika {SS}s";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1864; prefs.py:292; */
"OK" = "OK";
/* EDMarketConnector.py: The application is shutting down; In files: EDMarketConnector.py:1936; */
"Shutting down..." = "Sulkeutuu...";
/* EDMarketConnector.py: Popup-text about 'active' plugins without Python 3.x support; In files: EDMarketConnector.py:2253:2259; */
"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." = "Yksi tai useampi käyttämistäsi lisäosista ei sisällä vielä Pyhton 3.x -tukea. Tarkista tiedoston '{FILE}' > '{SETTINGS}' välilehti '{PLUGINS}' lisäosista, onko niistä uudempaa versiota saatavilla; muussa tapauksessa ota yhteyttä sen kehittäjään ja kerro, että sen lähdekoodi on päivitettävä Python 3.x -tasolle.\n\nVoit estää lisäosan toiminnan nimeämällä sen hakemiston siten, että se päättyy tekstiin '{DISABLED}'.";
/* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2263; prefs.py:986; */
"Plugins" = "Lisäosat";
/* EDMarketConnector.py: Popup window title for list of 'enabled' plugins that don't work with Python 3.x; In files: EDMarketConnector.py:2274; */
"EDMC: Plugins Without Python 3.x Support" = "EDMC: Lisäosat ilman Python 3.x -tukea";
/* journal_lock.py: Title text on popup when Journal directory already locked; In files: journal_lock.py:208; */
"Journal directory already locked" = "Journal -hakemisto on jo lukittu";
/* journal_lock.py: Text for when newly selected Journal directory is already locked; In files: journal_lock.py:225:226; */
"The new Journal Directory location is already locked.{CR}You can either attempt to resolve this and then Retry, or choose to Ignore this." = "Uusi Journal -hakemisto on jo lukittu.{CR}Voit joko yrittää ratkaista tämän ja yrittää uudelleen, tai jättää asian huomiotta.";
/* journal_lock.py: Generic 'Retry' button label; In files: journal_lock.py:230; */
"Retry" = "Yritä uudelleen";
/* journal_lock.py: Generic 'Ignore' button label; In files: journal_lock.py:234; */
"Ignore" = "Älä huomioi";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:193; prefs.py:455; prefs.py:709; prefs.py:742; */
"Default" = "Oletus";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:251; */
/* eddb.py: Journal Processing disabled due to an active killswitch; In files: eddb.py:179; */
"EDDB Journal processing disabled. See Log." = "EDDB lokikirjan käsittely ei käytössä. Katso lokitiedot.";
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:458; eddn.py:2413; eddn.py:2451; eddn.py:2519; */
"Error: Can't connect to EDDN" = "Virhe: ei saatu yhteyttä EDDN:ään";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:2041; */
"Send station data to the Elite Dangerous Data Network" = "Lähetä asematiedot Elite Dangerous Data Network -palveluun";
/* eddn.py: Enable EDDN support for system and other scan data checkbox label; In files: eddn.py:2052; */
"Send system and scan data to the Elite Dangerous Data Network" = "Lähetä järjestelmä- ja skannaustiedot Elite Dangerous Data Network -palveluun";
/* eddn.py: EDDN delay sending until docked option is on, this message notes that a send was skipped due to this; In files: eddn.py:2063; */
"Delay sending until docked" = "Lähetä tiedot vasta telakoitumisen jälkeen";
/* eddn.py: Killswitch disabled EDDN; In files: eddn.py:2178; */
"EDDN journal handler disabled. See Log." = "EDDB lokikirjan käsittelijä ei käytössä. Katso lokitiedot.";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:2507; */
"Sending data to EDDN..." = "Lähetetään tietoja EDDN:ään...";
/* edsm.py: Settings>EDSM - Label on checkbox for 'send data'; In files: edsm.py:308; */
"Send flight log and Cmdr status to EDSM" = "Lähetä lentoloki ja komentajatiedot EDSM:ään";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:319; */
"Elite Dangerous Star Map credentials" = "Elite Dangerous Star Map -kirjautuminen";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:341; */
"Commander Name" = "Komentajanimi";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:350; inara.py:278; */
"API Key" = "API avain";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:394; prefs.py:527; prefs.py:1157; prefs.py:1190; stats.py:154; stats.py:173; stats.py:192; stats.py:209; */
"None" = "Ei määritelty";
/* edsm.py: EDSM plugin - Journal handling disabled by killswitch; In files: edsm.py:516; */
"EDSM Handler disabled. See Log." = "ESDM käsittelijä ei käytössä. Katso lokitiedot.";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:916; edsm.py:1048; */
"Error: EDSM {MSG}" = "Virhe: EDSM {MSG}";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:953; edsm.py:1043; */
"Error: Can't connect to EDSM" = "Virhe: ei saatu yhteyttä EDSM:ään";
/* inara.py: Checkbox to enable INARA API Usage; In files: inara.py:257; */
"Send flight log and Cmdr status to Inara" = "Lähetä lentoloki ja komentajatiedot Inaraan";
/* inara.py: Text for INARA API keys link ( goes to https://inara.cz/settings-api ); In files: inara.py:269; */
"Inara credentials" = "Inaran kirjautuminen";
/* inara.py: INARA support disabled via killswitch; In files: inara.py:395; */
"Inara disabled. See Log." = "Inara ei käytössä. Katso lokitiedot.";
/* inara.py: INARA API returned some kind of error (error message will be contained in {MSG}); In files: inara.py:1650; inara.py:1663; */
"Error: Inara {MSG}" = "Inaran virhe: {MSG}";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:237; */
"Preferences" = "Valinnat";
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:348; */
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:335; */
"Please choose what data to save" = "Valitse mitkä tiedot tallennetaan";
/* prefs.py: Settings > Output option; In files: prefs.py:354; */
/* prefs.py: Settings > Output option; In files: prefs.py:341; */
"Market data in CSV format file" = "Markkinadata CSV -formaatissa";
/* prefs.py: Settings > Output option; In files: prefs.py:363; */
/* prefs.py: Settings > Output option; In files: prefs.py:350; */
"Market data in Trade Dangerous format file" = "Markkinadata Trade Dangerous -formaatissa";
/* prefs.py: Settings > Output option; In files: prefs.py:373; */
/* prefs.py: Settings > Output option; In files: prefs.py:360; */
"Ship loadout" = "Aluksen konfiguraatio";
/* prefs.py: Settings > Output option; In files: prefs.py:383; */
/* prefs.py: Settings > Output option; In files: prefs.py:370; */
"Automatically update on docking" = "Päivitä automaattisesti telakoituessa";
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:392; prefs.py:411; */
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:379; prefs.py:398; */
"File location" = "Tiedostosijainti";
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:400; prefs.py:450; */
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:387; prefs.py:437; */
"Change..." = "Muuta...";
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:403; prefs.py:453; */
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:390; prefs.py:440; */
"Browse..." = "Selaa...";
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:418; */
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:405; */
"Output" = "Tallennus";
/* prefs.py: Settings > Configuration - Label for Journal files location; In files: prefs.py:444; prefs.py:459; */
/* prefs.py: Settings > Configuration - Label for Journal files location; In files: prefs.py:431; prefs.py:446; */
"E:D journal file location" = "E:D lokikirjan tiedostosijainti";
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:484; */
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:490; */
"Keyboard shortcut" = "Näppäimistöoikopolku";
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:486; */
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:492; */
"Hotkey" = "Pikanäppäin";
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:495; */
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:501; */
"Re-start {APP} to use shortcuts" = "Käynnistä {APP} uudelleen käyttääksesi pikanäppäimiä";
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:504; */
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:510; */
"{APP} needs permission to use shortcuts" = "{APP} tarvitsee lupasi pikakuvakkeiden käyttämiseen";
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:509; */
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:515; */
"Open System Preferences" = "Avaa järjestelmän asetukset";
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:531; */
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:538; */
"Only when Elite: Dangerous is the active app" = "Vain jos Elite:Dangerous on käynnissä";
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:542; */
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:549; */
"Play sound" = "Toista ääni";
/* prefs.py: Configuration - disable checks for app updates when in-game; In files: prefs.py:557; */
/* prefs.py: Configuration - disable checks for app updates when in-game; In files: prefs.py:564; */
"Disable Automatic Application Updates Check when in-game" = "Estä automaattiset päivitystarkastukset pelaamisen aikana";
/* prefs.py: Label for preferred shipyard, system and station 'providers'; In files: prefs.py:570; */
/* prefs.py: Label for preferred shipyard, system and station 'providers'; In files: prefs.py:577; */
"Preferred websites" = "Ensisijaiset sivustot";
/* prefs.py: Label for Shipyard provider selection; In files: prefs.py:581; */
/* prefs.py: Label for Shipyard provider selection; In files: prefs.py:588; */
"Shipyard" = "Telakka";
/* prefs.py: Label for checkbox to utilise alternative Coriolis URL method; In files: prefs.py:593; */
/* prefs.py: Label for checkbox to utilise alternative Coriolis URL method; In files: prefs.py:600; */
"Use alternate URL method" = "Käytä vaihtoehtoista URL-metodia";
/* prefs.py: Configuration - Label for selection of Log Level; In files: prefs.py:646; */
/* prefs.py: Configuration - Label for selection of Log Level; In files: prefs.py:653; */
"Log Level" = "Lokin syvyys";
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:674; */
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:681; */
"Configuration" = "Valinnat";
/* prefs.py: Preferences privacy tab title; In files: prefs.py:696; */
/* prefs.py: Preferences privacy tab title; In files: prefs.py:703; */
"Privacy" = "Yksityisyys";
/* prefs.py: Label for Settings > Appeareance > selection of 'normal' text colour; In files: prefs.py:709; */
/* prefs.py: Label for Settings > Appeareance > selection of 'normal' text colour; In files: prefs.py:716; */
"Normal text" = "Normaali teksti";
/* prefs.py: Label for Settings > Appeareance > selection of 'highlightes' text colour; In files: prefs.py:711; */
/* prefs.py: Label for Settings > Appeareance > selection of 'highlightes' text colour; In files: prefs.py:718; */
"Highlighted text" = "Korostettu teksti";
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:720; */
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:727; */
"Language" = "Kieli";
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:730; */
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:737; */
"Theme" = "Teema";
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:741; */
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:749; */
"Dark" = "Tumma";
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:748; */
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:756; */
"Transparent" = "Läpinäkyvä";
/* prefs.py: Appearance - Label for selection of UI scaling; In files: prefs.py:794; */
/* prefs.py: Appearance - Label for selection of UI scaling; In files: prefs.py:802; */
"UI Scale Percentage" = "Käyttöliittymän skaalausprosentti";
/* prefs.py: Appearance - Help/hint text for UI scaling selection; In files: prefs.py:815; */
/* prefs.py: Appearance - Help/hint text for UI scaling selection; In files: prefs.py:823; */
"100 means Default{CR}Restart Required for{CR}changes to take effect!" = "100 tarkoittaa oletusta{CR}Muutosten käyttöönotto{CR}edellyttää uudelleenkäynnistystä!";
/* prefs.py: Appearance - Label for selection of main window transparency; In files: prefs.py:825; */
/* prefs.py: Appearance - Label for selection of main window transparency; In files: prefs.py:833; */
"Main window transparency" = "Pääikkunan läpinäkyvyys";
/* prefs.py: Appearance - Help/hint text for Main window transparency selection; In files: prefs.py:845:848; */
/* prefs.py: Appearance - Help/hint text for Main window transparency selection; In files: prefs.py:853:856; */
"100 means fully opaque.{CR}Window is updated in real time" = "100 tarkoittaa täysin peittävää.{CR}Ikkuna päivittyy reaaliajassa.";
/* prefs.py: Appearance option for Windows "minimize to system tray"; In files: prefs.py:877; */
/* prefs.py: Appearance option for Windows "minimize to system tray"; In files: prefs.py:885; */
"Minimize to system tray" = "Pienennä tehtäväpalkkiin";
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:885; */
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:893; */
"Appearance" = "Ulkoasu";
/* prefs.py: Label for location of third-party plugins folder; In files: prefs.py:900; */
/* prefs.py: Label for location of third-party plugins folder; In files: prefs.py:908; */
"Plugins folder" = "Lisäosien hakemistosijainti";
/* prefs.py: Label on button used to open a filesystem folder; In files: prefs.py:907; */
/* prefs.py: Label on button used to open a filesystem folder; In files: prefs.py:915; */
"Open" = "Avaa";
/* prefs.py: Tip/label about how to disable plugins; In files: prefs.py:915; */
/* prefs.py: Tip/label about how to disable plugins; In files: prefs.py:923; */
"Tip: You can disable a plugin by{CR}adding '{EXT}' to its folder name" = "Vihje: Voit poistaa lisäosan käytöstä{CR}lisäämällä '{EXT}' sen hakemistonimeen";
/* prefs.py: Label on list of enabled plugins; In files: prefs.py:926; */
/* prefs.py: Label on list of enabled plugins; In files: prefs.py:934; */
"Enabled Plugins" = "Käytössä olevat liitännäiset";
/* prefs.py: Plugins - Label for list of 'enabled' plugins that don't work with Python 3.x; In files: prefs.py:946; */
/* prefs.py: Plugins - Label for list of 'enabled' plugins that don't work with Python 3.x; In files: prefs.py:954; */
"Plugins Without Python 3.x Support:" = "Lisäosat, joissa ei ole Python 3.x -tukea:";
/* prefs.py: Plugins - Label on URL to documentation about migrating plugins from Python 2.7; In files: prefs.py:954; */
/* prefs.py: Plugins - Label on URL to documentation about migrating plugins from Python 2.7; In files: prefs.py:962; */
"Information on migrating plugins" = "Tietoja lisäosien siirrosta";
/* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:969; */
/* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:977; */
"Disabled Plugins" = "Estetyt liitännäiset";
/* stats.py: Cmdr stats; In files: stats.py:60; */
/* stats.py: Cmdr stats; In files: stats.py:58; */
"Balance" = "Saldo";
/* stats.py: Cmdr stats; In files: stats.py:61; */
/* stats.py: Cmdr stats; In files: stats.py:59; */
"Loan" = "Lainaa jäljellä";
/* stats.py: Top rank; In files: stats.py:65; */
/* stats.py: Top rank; In files: stats.py:63; */
"Elite" = "Elite";
/* stats.py: Ranking; In files: stats.py:76; */
/* stats.py: Ranking; In files: stats.py:74; */
"Combat" = "Taistelija -ranking";
/* stats.py: Ranking; In files: stats.py:77; */
/* stats.py: Ranking; In files: stats.py:75; */
"Trade" = "Kaupankäynti -ranking";
/* stats.py: Ranking; In files: stats.py:78; */
/* stats.py: Ranking; In files: stats.py:76; */
"Explorer" = "Tutkimusmatkailija -ranking";
/* stats.py: Ranking; In files: stats.py:81; */
/* stats.py: Ranking; In files: stats.py:79; */
"CQC" = "CQC -ranking";
/* stats.py: Ranking; In files: stats.py:82; */
/* stats.py: Ranking; In files: stats.py:80; */
"Federation" = "Federaation sotilasarvosi";
/* stats.py: Ranking; In files: stats.py:83; */
/* stats.py: Ranking; In files: stats.py:81; */
"Empire" = "Imperiumin sotilasarvosi";
/* stats.py: Ranking; In files: stats.py:84; */
/* stats.py: Ranking; In files: stats.py:82; */
"Powerplay" = "Valtapeli -ranking";
/* stats.py: Combat rank; In files: stats.py:93; */
/* stats.py: Combat rank; In files: stats.py:91; */
"Harmless" = "Harmless";
/* stats.py: Combat rank; In files: stats.py:94; */
/* stats.py: Combat rank; In files: stats.py:92; */
"Mostly Harmless" = "Mostly Harmless";
/* stats.py: Combat rank; In files: stats.py:95; */
/* stats.py: Combat rank; In files: stats.py:93; */
"Novice" = "Novice";
/* stats.py: Combat rank; In files: stats.py:96; */
/* stats.py: Combat rank; In files: stats.py:94; */
"Competent" = "Competent";
/* stats.py: Combat rank; In files: stats.py:97; */
/* stats.py: Combat rank; In files: stats.py:95; */
"Expert" = "Expert";
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:98; stats.py:178; */
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:96; stats.py:176; */
"Master" = "Master";
/* stats.py: Combat rank; In files: stats.py:99; */
/* stats.py: Combat rank; In files: stats.py:97; */
"Dangerous" = "Dangerous";
/* stats.py: Combat rank; In files: stats.py:100; */
/* stats.py: Combat rank; In files: stats.py:98; */
"Deadly" = "Deadly";
/* stats.py: Trade rank; In files: stats.py:103; */
/* stats.py: Trade rank; In files: stats.py:101; */
"Penniless" = "Penniless";
/* stats.py: Trade rank; In files: stats.py:104; */
/* stats.py: Trade rank; In files: stats.py:102; */
"Mostly Penniless" = "Mostly Penniless";
/* stats.py: Trade rank; In files: stats.py:105; */
/* stats.py: Trade rank; In files: stats.py:103; */
"Peddler" = "Peddler";
/* stats.py: Trade rank; In files: stats.py:106; */
/* stats.py: Trade rank; In files: stats.py:104; */
"Dealer" = "Dealer";
/* stats.py: Trade rank; In files: stats.py:107; */
/* stats.py: Trade rank; In files: stats.py:105; */
"Merchant" = "Merchant";
/* stats.py: Trade rank; In files: stats.py:108; */
/* stats.py: Trade rank; In files: stats.py:106; */
"Broker" = "Broker";
/* stats.py: Trade rank; In files: stats.py:109; */
/* stats.py: Trade rank; In files: stats.py:107; */
"Entrepreneur" = "Entrepreneur";
/* stats.py: Trade rank; In files: stats.py:110; */
/* stats.py: Trade rank; In files: stats.py:108; */
"Tycoon" = "Tycoon";
/* stats.py: Explorer rank; In files: stats.py:113; */
/* stats.py: Explorer rank; In files: stats.py:111; */
"Aimless" = "Aimless";
/* stats.py: Explorer rank; In files: stats.py:114; */
/* stats.py: Explorer rank; In files: stats.py:112; */
"Mostly Aimless" = "Mostly Aimless";
/* stats.py: Explorer rank; In files: stats.py:115; */
/* stats.py: Explorer rank; In files: stats.py:113; */
"Scout" = "Scout";
/* stats.py: Explorer rank; In files: stats.py:116; */
/* stats.py: Explorer rank; In files: stats.py:114; */
"Surveyor" = "Surveyor";
/* stats.py: Explorer rank; In files: stats.py:117; */
/* stats.py: Explorer rank; In files: stats.py:115; */
"Trailblazer" = "Trailblazer";
/* stats.py: Explorer rank; In files: stats.py:118; */
/* stats.py: Explorer rank; In files: stats.py:116; */
"Pathfinder" = "Pathfinder";
/* stats.py: Explorer rank; In files: stats.py:119; */
/* stats.py: Explorer rank; In files: stats.py:117; */
"Ranger" = "Ranger";
/* stats.py: Explorer rank; In files: stats.py:120; */
/* stats.py: Explorer rank; In files: stats.py:118; */
"Pioneer" = "Pioneer";
/* stats.py: CQC rank; In files: stats.py:144; */
/* stats.py: CQC rank; In files: stats.py:142; */
"Helpless" = "Helpless";
/* stats.py: CQC rank; In files: stats.py:145; */
/* stats.py: CQC rank; In files: stats.py:143; */
"Mostly Helpless" = "Mostly Helpless";
/* stats.py: CQC rank; In files: stats.py:146; */
/* stats.py: CQC rank; In files: stats.py:144; */
"Amateur" = "Amateur";
/* stats.py: CQC rank; In files: stats.py:147; */
/* stats.py: CQC rank; In files: stats.py:145; */
"Semi Professional" = "Semi Professional";
/* stats.py: CQC rank; In files: stats.py:148; */
/* stats.py: CQC rank; In files: stats.py:146; */
"Professional" = "Professional";
/* stats.py: CQC rank; In files: stats.py:149; */
/* stats.py: CQC rank; In files: stats.py:147; */
"Champion" = "Champion";
/* stats.py: CQC rank; In files: stats.py:150; */
/* stats.py: CQC rank; In files: stats.py:148; */
"Hero" = "Hero";
/* stats.py: CQC rank; In files: stats.py:151; */
/* stats.py: CQC rank; In files: stats.py:149; */
"Gladiator" = "Gladiator";
/* stats.py: Federation rank; In files: stats.py:157; */
/* stats.py: Federation rank; In files: stats.py:155; */
"Recruit" = "Recruit";
/* stats.py: Federation rank; In files: stats.py:158; */
/* stats.py: Federation rank; In files: stats.py:156; */
"Cadet" = "Cadet";
/* stats.py: Federation rank; In files: stats.py:159; */
/* stats.py: Federation rank; In files: stats.py:157; */
"Midshipman" = "Midshipman";
/* stats.py: Federation rank; In files: stats.py:160; */
/* stats.py: Federation rank; In files: stats.py:158; */
"Petty Officer" = "Petty Officer";
/* stats.py: Federation rank; In files: stats.py:161; */
/* stats.py: Federation rank; In files: stats.py:159; */
"Chief Petty Officer" = "Chief Petty Officer";
/* stats.py: Federation rank; In files: stats.py:162; */
/* stats.py: Federation rank; In files: stats.py:160; */
"Warrant Officer" = "Warrant Officer";
/* stats.py: Federation rank; In files: stats.py:163; */
/* stats.py: Federation rank; In files: stats.py:161; */
"Ensign" = "Ensign";
/* stats.py: Federation rank; In files: stats.py:164; */
/* stats.py: Federation rank; In files: stats.py:162; */
"Lieutenant" = "Lieutenant";
/* stats.py: Federation rank; In files: stats.py:165; */
/* stats.py: Federation rank; In files: stats.py:163; */
"Lieutenant Commander" = "Lieutenant Commander";
/* stats.py: Federation rank; In files: stats.py:166; */
/* stats.py: Federation rank; In files: stats.py:164; */
"Post Commander" = "Post Commander";
/* stats.py: Federation rank; In files: stats.py:167; */
/* stats.py: Federation rank; In files: stats.py:165; */
"Post Captain" = "Post Captain";
/* stats.py: Federation rank; In files: stats.py:168; */
/* stats.py: Federation rank; In files: stats.py:166; */
"Rear Admiral" = "Rear Admiral";
/* stats.py: Federation rank; In files: stats.py:169; */
/* stats.py: Federation rank; In files: stats.py:167; */
"Vice Admiral" = "Vice Admiral";
/* stats.py: Federation rank; In files: stats.py:170; */
/* stats.py: Federation rank; In files: stats.py:168; */
"Admiral" = "Admiral";
/* stats.py: Empire rank; In files: stats.py:176; */
/* stats.py: Empire rank; In files: stats.py:174; */
"Outsider" = "Outsider";
/* stats.py: Empire rank; In files: stats.py:177; */
/* stats.py: Empire rank; In files: stats.py:175; */
"Serf" = "Serf";
/* stats.py: Empire rank; In files: stats.py:179; */
/* stats.py: Empire rank; In files: stats.py:177; */
"Squire" = "Squire";
/* stats.py: Empire rank; In files: stats.py:180; */
/* stats.py: Empire rank; In files: stats.py:178; */
"Knight" = "Knight";
/* stats.py: Empire rank; In files: stats.py:181; */
/* stats.py: Empire rank; In files: stats.py:179; */
"Lord" = "Lord";
/* stats.py: Empire rank; In files: stats.py:182; */
/* stats.py: Empire rank; In files: stats.py:180; */
"Baron" = "Baron";
/* stats.py: Empire rank; In files: stats.py:183; */
/* stats.py: Empire rank; In files: stats.py:181; */
"Viscount" = "Viscount";
/* stats.py: Empire rank; In files: stats.py:184; */
/* stats.py: Empire rank; In files: stats.py:182; */
"Count" = "Count";
/* stats.py: Empire rank; In files: stats.py:185; */
/* stats.py: Empire rank; In files: stats.py:183; */
"Earl" = "Earl";
/* stats.py: Empire rank; In files: stats.py:186; */
/* stats.py: Empire rank; In files: stats.py:184; */
"Marquis" = "Marquis";
/* stats.py: Empire rank; In files: stats.py:187; */
/* stats.py: Empire rank; In files: stats.py:185; */
"Duke" = "Duke";
/* stats.py: Empire rank; In files: stats.py:188; */
/* stats.py: Empire rank; In files: stats.py:186; */
"Prince" = "Prince";
/* stats.py: Empire rank; In files: stats.py:189; */
/* stats.py: Empire rank; In files: stats.py:187; */
"King" = "King";
/* stats.py: Power rank; In files: stats.py:195; */
/* stats.py: Power rank; In files: stats.py:193; */
"Rating 1" = "Luokitus 1";
/* stats.py: Power rank; In files: stats.py:196; */
/* stats.py: Power rank; In files: stats.py:194; */
"Rating 2" = "Luokitus 2";
/* stats.py: Power rank; In files: stats.py:197; */
/* stats.py: Power rank; In files: stats.py:195; */
"Rating 3" = "Luokitus 3";
/* stats.py: Power rank; In files: stats.py:198; */
/* stats.py: Power rank; In files: stats.py:196; */
"Rating 4" = "Luokitus 4";
/* stats.py: Power rank; In files: stats.py:199; */
/* stats.py: Power rank; In files: stats.py:197; */
"Rating 5" = "Luokitus 5";
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:413; */
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:409; */
"Value" = "Arvo";
/* stats.py: Status dialog title; In files: stats.py:422; */
/* stats.py: Status dialog title; In files: stats.py:418; */
"Ships" = "Alukset";
+284 -284
View File
@@ -1,518 +1,518 @@
/* Language name */
"!Language" = "Français";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:212; eddn.py:584; eddn.py:925; load.py:215; load.py:593; load.py:940; eddn.py:252; eddn.py:699; eddn.py:1439; */
"Sending data to EDDN..." = "Envoi des données à EDDN...";
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:257; eddn.py:864; eddn.py:898; eddn.py:937; load.py:260; load.py:878; load.py:913; load.py:952; eddn.py:316; eddn.py:1375; eddn.py:1410; eddn.py:1451; */
"Error: Can't connect to EDDN" = "Erreur : Connexion impossible à EDDN";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:663; load.py:672; eddn.py:1122; */
"Send station data to the Elite Dangerous Data Network" = "Envoyer les données de la station à Elite Dangerous Data Network";
/* eddn.py: Enable EDDN support for system and other scan data checkbox label; In files: eddn.py:673; load.py:682; eddn.py:1133; */
"Send system and scan data to the Elite Dangerous Data Network" = "Envoyer les systèmes et les informations de scan à Elite Dangerous Data Network";
/* eddn.py: EDDN delay sending until docked option is on, this message notes that a send was skipped due to this; In files: eddn.py:683; load.py:692; eddn.py:1144; */
"Delay sending until docked" = "Retarder l'envoi jusqu'à l'amarrage";
/* edsm.py: Settings>EDSM - Label on checkbox for 'send data'; In files: edsm.py:197; load.py:197; edsm.py:237; */
"Send flight log and Cmdr status to EDSM" = "Envoyer le journal de vol et le statut Cmdr à EDSM";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:206; load.py:206; edsm.py:247; */
"Elite Dangerous Star Map credentials" = "Informations d'identification pour Elite Dangerous Star Map";
/* edsm.py: Game Commander name label in EDSM settings; theme.py: Label for commander name in main window; EDMarketConnector.py: Label for commander name in main window; stats.py: Cmdr stats; In files: edsm.py:216; load.py:216; edsm.py:258; theme.py:227; EDMarketConnector.py:822; stats.py:59; */
"Cmdr" = "Cmd";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:223; load.py:223; edsm.py:266; */
"Commander Name" = "Nom du commandant";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:230; inara.py:233; load.py:230; load.py:233; edsm.py:274; inara.py:243; */
"API Key" = "Clé API";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:256; load.py:256; edsm.py:301; prefs.py:520; prefs.py:1174; prefs.py:1207; stats.py:156; stats.py:175; stats.py:194; stats.py:211; */
"None" = "Aucun";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:622; edsm.py:724; load.py:627; load.py:729; edsm.py:747; edsm.py:875; */
"Error: EDSM {MSG}" = "Erreur : EDSM {MSG}";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:658; edsm.py:720; load.py:663; load.py:725; edsm.py:784; edsm.py:870; */
"Error: Can't connect to EDSM" = "Erreur : Connexion impossible à EDSM";
/* inara.py: Checkbox to enable INARA API Usage; In files: inara.py:215; load.py:215; inara.py:222; */
"Send flight log and Cmdr status to Inara" = "Envoyer le journal de vol et le statut Cmdr à Inara";
/* inara.py: Text for INARA API keys link ( goes to https://inara.cz/settings-api ); In files: inara.py:225; load.py:225; inara.py:234; */
"Inara credentials" = "Informations d'identification pour Inara";
/* inara.py: INARA API returned some kind of error (error message will be contained in {MSG}); In files: inara.py:1316; inara.py:1328; load.py:1319; load.py:1331; inara.py:1587; inara.py:1600; */
"Error: Inara {MSG}" = "Erreur : Inara {MSG}";
/* ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; In files: ttkHyperlinkLabel.py:42; EDMarketConnector.py:866; */
"Copy" = "Copier";
/* EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:529; EDMarketConnector.py:828; EDMarketConnector.py:1520; */
"Update" = "Mettre à jour";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:612; prefs.py:867; */
"Always on top" = "Toujours visible";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:824; EDMarketConnector.py:1279; */
"Role" = "Rôle";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:824; EDMarketConnector.py:1289; EDMarketConnector.py:1312; stats.py:409; */
"Ship" = "Vaisseau";
/* EDMarketConnector.py: Label for 'Suit' line in main UI; In files: EDMarketConnector.py:825; */
"Suit" = "Combinaison";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:826; prefs.py:607; stats.py:411; */
"System" = "Système";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:827; prefs.py:625; prefs.py:762; stats.py:412; */
"Station" = "Station";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:830; EDMarketConnector.py:845; EDMarketConnector.py:848; EDMarketConnector.py:2019; */
"File" = "Fichier";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:831; EDMarketConnector.py:846; EDMarketConnector.py:849; */
"Edit" = "Édition";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:832; */
"View" = "Présentation";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:833; */
"Window" = "Fenêtre";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:834; EDMarketConnector.py:847; EDMarketConnector.py:850; */
"Help" = "Aide";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:837; EDMarketConnector.py:863; EDMarketConnector.py:1566; */
"About {APP}" = "À propos de {APP}";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:839; EDMarketConnector.py:862; */
"Check for Updates..." = "Chercher une mise à jour...";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:840; EDMarketConnector.py:854; */
"Save Raw Data..." = "Sauvegarder les Données Brutes...";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:841; EDMarketConnector.py:853; stats.py:406; */
"Status" = "État";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:842; EDMarketConnector.py:860; */
"Privacy Policy" = "Politique de Confidentialité";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:843; EDMarketConnector.py:861; EDMarketConnector.py:1600; */
"Release Notes" = "Notes de publication";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:855; EDMarketConnector.py:2020; prefs.py:255; */
"Settings" = "Paramètres";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:856; */
"Exit" = "Quitter";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:859; */
"Documentation" = "Documentation";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:872; */
"Logging in..." = "Connexion en cours...";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:888; EDMarketConnector.py:1431; */
"Authentication successful" = "Authentification réussie";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:919; */
"You're not docked at a station!" = "Vous n'êtes pas amarré à une station !";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:927; */
"Station doesn't have anything!" = "La station n'a rien du tout !";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:932; */
"Station doesn't have a market!" = "La station n'a pas de marché !";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1006; */
"Fetching data..." = "Récupération des données...";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1051; stats.py:335; */
"Who are you?!" = "Qui êtes-vous ?";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1057; stats.py:345; */
"Where are you?!" = "Où êtes-vous ?";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1064; stats.py:353; */
"What are you flying?!" = "Avec quoi volez-vous ?";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1226; */
"Last updated at %H:%M:%S" = "Dernière mise à jour à %H:%M:%S";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1254; */
"Fighter" = "Chasseur";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1255; */
"Gunner" = "Artilleur";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1256; */
"Helm" = "Pilote";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1514; */
"cooldown {SS}s" = "Temps de recharge {SS}s";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1626; prefs.py:306; */
"OK" = "OK";
/* EDMarketConnector.py: Popup-text about 'active' plugins without Python 3.x support; In files: EDMarketConnector.py:2008:2014; */
"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." = "Un ou plusieurs de vos plugins activés ne sont pas encore compatibles avec Python 3.x. Regardez la liste sur l'onglet '{PLUGINS}' de '{FILE}' > '{SETTINGS}'. Vous devriez vérifier s'il y a une version mise à jour, sinon alertez le développeur qu'il doit mettre à jour le code pour Python 3.x.\n\nVous pouvez désactiver un plugin en renommant son dossier pour qu'il ait '{DISABLED}' à la fin du nom.";
/* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2018; prefs.py:978; */
"Plugins" = "Plugins";
/* EDMarketConnector.py: Popup window title for list of 'enabled' plugins that don't work with Python 3.x; In files: EDMarketConnector.py:2029; */
"EDMC: Plugins Without Python 3.x Support" = "EDMC: Plugins non compatibles avec Python 3.x";
/* companion.py: Frontier CAPI data doesn't agree with latest Journal game location; In files: companion.py:237; */
/* companion.py: Frontier CAPI data doesn't agree with latest Journal game location; In files: companion.py:245; */
"Error: Frontier server is lagging" = "Erreur : Le serveur Frontier ne répond pas";
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:263; */
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:271; */
"Error: Invalid Credentials" = "Erreur : Identifiants invalides";
/* companion.py: Frontier CAPI authorisation not for currently game-active commander; In files: companion.py:288; */
/* companion.py: Frontier CAPI authorisation not for currently game-active commander; In files: companion.py:296; */
"Error: Wrong Cmdr" = "Erreur : Cmdr incorrect";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:194; prefs.py:468; prefs.py:702; prefs.py:735; */
/* EDMarketConnector.py: Main UI Update button; EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:601; EDMarketConnector.py:919; EDMarketConnector.py:1748; */
"Update" = "Mettre à jour";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:710; prefs.py:875; */
"Always on top" = "Toujours visible";
/* EDMarketConnector.py: Label for commander name in main window; edsm.py: Game Commander name label in EDSM settings; stats.py: Cmdr stats; theme.py: Label for commander name in main window; In files: EDMarketConnector.py:913; edsm.py:332; stats.py:57; theme.py:290; */
"Cmdr" = "Cmd";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:915; EDMarketConnector.py:1487; */
"Role" = "Rôle";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:915; EDMarketConnector.py:1497; EDMarketConnector.py:1520; stats.py:405; */
"Ship" = "Vaisseau";
/* EDMarketConnector.py: Label for 'Suit' line in main UI; In files: EDMarketConnector.py:916; */
"Suit" = "Combinaison";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:917; prefs.py:614; stats.py:407; */
"System" = "Système";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:918; prefs.py:632; prefs.py:770; stats.py:408; */
"Station" = "Station";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:921; EDMarketConnector.py:939; EDMarketConnector.py:942; EDMarketConnector.py:2264; */
"File" = "Fichier";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:922; EDMarketConnector.py:940; EDMarketConnector.py:943; */
"Edit" = "Édition";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:923; */
"View" = "Présentation";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:924; */
"Window" = "Fenêtre";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:925; EDMarketConnector.py:941; EDMarketConnector.py:944; */
"Help" = "Aide";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:928; EDMarketConnector.py:959; EDMarketConnector.py:1804; */
"About {APP}" = "À propos de {APP}";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:930; EDMarketConnector.py:958; */
"Check for Updates..." = "Chercher une mise à jour...";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:931; EDMarketConnector.py:948; */
"Save Raw Data..." = "Sauvegarder les Données Brutes...";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:932; EDMarketConnector.py:947; stats.py:402; */
"Status" = "État";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:933; EDMarketConnector.py:953; */
"Documentation" = "Documentation";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:936; EDMarketConnector.py:956; */
"Privacy Policy" = "Politique de Confidentialité";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:937; EDMarketConnector.py:957; EDMarketConnector.py:1838; */
"Release Notes" = "Notes de publication";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:949; EDMarketConnector.py:2265; prefs.py:241; */
"Settings" = "Paramètres";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:950; */
"Exit" = "Quitter";
/* EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; In files: EDMarketConnector.py:962; ttkHyperlinkLabel.py:53; */
"Copy" = "Copier";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:978; */
"Logging in..." = "Connexion en cours...";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:994; EDMarketConnector.py:1657; */
"Authentication successful" = "Authentification réussie";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:1025; */
"You're not docked at a station!" = "Vous n'êtes pas amarré à une station !";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:1033; */
"Station doesn't have anything!" = "La station n'a rien du tout !";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:1038; */
"Station doesn't have a market!" = "La station n'a pas de marché !";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1128; EDMarketConnector.py:1179; */
"Fetching data..." = "Récupération des données...";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1246; stats.py:333; */
"Who are you?!" = "Qui êtes-vous ?";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1252; stats.py:341; */
"Where are you?!" = "Où êtes-vous ?";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1259; stats.py:349; */
"What are you flying?!" = "Avec quoi volez-vous ?";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1434; */
"Last updated at %H:%M:%S" = "Dernière mise à jour à %H:%M:%S";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1462; */
"Fighter" = "Chasseur";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1463; */
"Gunner" = "Artilleur";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1464; */
"Helm" = "Pilote";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1742; */
"cooldown {SS}s" = "Temps de recharge {SS}s";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1864; prefs.py:292; */
"OK" = "OK";
/* EDMarketConnector.py: Popup-text about 'active' plugins without Python 3.x support; In files: EDMarketConnector.py:2253:2259; */
"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." = "Un ou plusieurs de vos plugins activés ne sont pas encore compatibles avec Python 3.x. Regardez la liste sur l'onglet '{PLUGINS}' de '{FILE}' > '{SETTINGS}'. Vous devriez vérifier s'il y a une version mise à jour, sinon alertez le développeur qu'il doit mettre à jour le code pour Python 3.x.\n\nVous pouvez désactiver un plugin en renommant son dossier pour qu'il ait '{DISABLED}' à la fin du nom.";
/* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2263; prefs.py:986; */
"Plugins" = "Plugins";
/* EDMarketConnector.py: Popup window title for list of 'enabled' plugins that don't work with Python 3.x; In files: EDMarketConnector.py:2274; */
"EDMC: Plugins Without Python 3.x Support" = "EDMC: Plugins non compatibles avec Python 3.x";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:193; prefs.py:455; prefs.py:709; prefs.py:742; */
"Default" = "Par défaut";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:251; */
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:458; eddn.py:2413; eddn.py:2451; eddn.py:2519; */
"Error: Can't connect to EDDN" = "Erreur : Connexion impossible à EDDN";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:2041; */
"Send station data to the Elite Dangerous Data Network" = "Envoyer les données de la station à Elite Dangerous Data Network";
/* eddn.py: Enable EDDN support for system and other scan data checkbox label; In files: eddn.py:2052; */
"Send system and scan data to the Elite Dangerous Data Network" = "Envoyer les systèmes et les informations de scan à Elite Dangerous Data Network";
/* eddn.py: EDDN delay sending until docked option is on, this message notes that a send was skipped due to this; In files: eddn.py:2063; */
"Delay sending until docked" = "Retarder l'envoi jusqu'à l'amarrage";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:2507; */
"Sending data to EDDN..." = "Envoi des données à EDDN...";
/* edsm.py: Settings>EDSM - Label on checkbox for 'send data'; In files: edsm.py:308; */
"Send flight log and Cmdr status to EDSM" = "Envoyer le journal de vol et le statut Cmdr à EDSM";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:319; */
"Elite Dangerous Star Map credentials" = "Informations d'identification pour Elite Dangerous Star Map";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:341; */
"Commander Name" = "Nom du commandant";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:350; inara.py:278; */
"API Key" = "Clé API";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:394; prefs.py:527; prefs.py:1157; prefs.py:1190; stats.py:154; stats.py:173; stats.py:192; stats.py:209; */
"None" = "Aucun";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:916; edsm.py:1048; */
"Error: EDSM {MSG}" = "Erreur : EDSM {MSG}";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:953; edsm.py:1043; */
"Error: Can't connect to EDSM" = "Erreur : Connexion impossible à EDSM";
/* inara.py: Checkbox to enable INARA API Usage; In files: inara.py:257; */
"Send flight log and Cmdr status to Inara" = "Envoyer le journal de vol et le statut Cmdr à Inara";
/* inara.py: Text for INARA API keys link ( goes to https://inara.cz/settings-api ); In files: inara.py:269; */
"Inara credentials" = "Informations d'identification pour Inara";
/* inara.py: INARA API returned some kind of error (error message will be contained in {MSG}); In files: inara.py:1650; inara.py:1663; */
"Error: Inara {MSG}" = "Erreur : Inara {MSG}";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:237; */
"Preferences" = "Préférences";
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:348; */
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:335; */
"Please choose what data to save" = "Veuillez choisir les données à sauvegarder";
/* prefs.py: Settings > Output option; In files: prefs.py:354; */
/* prefs.py: Settings > Output option; In files: prefs.py:341; */
"Market data in CSV format file" = "Données du marché au format CSV";
/* prefs.py: Settings > Output option; In files: prefs.py:363; */
/* prefs.py: Settings > Output option; In files: prefs.py:350; */
"Market data in Trade Dangerous format file" = "Données du marché au format Trade Dangerous";
/* prefs.py: Settings > Output option; In files: prefs.py:373; */
/* prefs.py: Settings > Output option; In files: prefs.py:360; */
"Ship loadout" = "Équipements du vaisseau";
/* prefs.py: Settings > Output option; In files: prefs.py:383; */
/* prefs.py: Settings > Output option; In files: prefs.py:370; */
"Automatically update on docking" = "Mettre à jour automatiquement à l'amarrage";
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:392; prefs.py:411; */
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:379; prefs.py:398; */
"File location" = "Emplacement des fichiers";
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:400; prefs.py:450; */
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:387; prefs.py:437; */
"Change..." = "Spécifier...";
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:403; prefs.py:453; */
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:390; prefs.py:440; */
"Browse..." = "Parcourir...";
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:418; */
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:405; */
"Output" = "Sorties";
/* prefs.py: Settings > Configuration - Label for Journal files location; In files: prefs.py:444; prefs.py:459; */
/* prefs.py: Settings > Configuration - Label for Journal files location; In files: prefs.py:431; prefs.py:446; */
"E:D journal file location" = "Emplacement du journal E:D";
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:484; */
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:490; */
"Keyboard shortcut" = "Raccourci clavier";
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:486; */
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:492; */
"Hotkey" = "Raccourci clavier";
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:495; */
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:501; */
"Re-start {APP} to use shortcuts" = "Rédémarrez {APP} pour utiliser les raccourcis";
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:504; */
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:510; */
"{APP} needs permission to use shortcuts" = "{APP} a besoin de permissions pour utiliser les raccourcis";
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:509; */
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:515; */
"Open System Preferences" = "Ouvrir Préférences Système";
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:531; */
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:538; */
"Only when Elite: Dangerous is the active app" = "Seulement quand Elite: Dangerous est l'application active";
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:542; */
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:549; */
"Play sound" = "Jouer un son";
/* prefs.py: Configuration - disable checks for app updates when in-game; In files: prefs.py:557; */
/* prefs.py: Configuration - disable checks for app updates when in-game; In files: prefs.py:564; */
"Disable Automatic Application Updates Check when in-game" = "Désactiver la vérification automatique des mises à jour quand vous êtes en jeu";
/* prefs.py: Label for preferred shipyard, system and station 'providers'; In files: prefs.py:570; */
/* prefs.py: Label for preferred shipyard, system and station 'providers'; In files: prefs.py:577; */
"Preferred websites" = "Sites préférés";
/* prefs.py: Label for Shipyard provider selection; In files: prefs.py:581; */
/* prefs.py: Label for Shipyard provider selection; In files: prefs.py:588; */
"Shipyard" = "Chantier naval";
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:674; */
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:681; */
"Configuration" = "Configuration";
/* prefs.py: Preferences privacy tab title; In files: prefs.py:696; */
/* prefs.py: Preferences privacy tab title; In files: prefs.py:703; */
"Privacy" = "Confidentialité";
/* prefs.py: Label for Settings > Appeareance > selection of 'normal' text colour; In files: prefs.py:709; */
/* prefs.py: Label for Settings > Appeareance > selection of 'normal' text colour; In files: prefs.py:716; */
"Normal text" = "Texte normal";
/* prefs.py: Label for Settings > Appeareance > selection of 'highlightes' text colour; In files: prefs.py:711; */
/* prefs.py: Label for Settings > Appeareance > selection of 'highlightes' text colour; In files: prefs.py:718; */
"Highlighted text" = "Texte en surbrillance";
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:720; */
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:727; */
"Language" = "Langue";
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:730; */
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:737; */
"Theme" = "Thème";
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:741; */
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:749; */
"Dark" = "Foncé";
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:748; */
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:756; */
"Transparent" = "Transparent";
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:885; */
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:893; */
"Appearance" = "Apparence";
/* prefs.py: Label for location of third-party plugins folder; In files: prefs.py:900; */
/* prefs.py: Label for location of third-party plugins folder; In files: prefs.py:908; */
"Plugins folder" = "Répertoire des plugins";
/* prefs.py: Label on button used to open a filesystem folder; In files: prefs.py:907; */
/* prefs.py: Label on button used to open a filesystem folder; In files: prefs.py:915; */
"Open" = "Ouvrir";
/* prefs.py: Tip/label about how to disable plugins; In files: prefs.py:915; */
/* prefs.py: Tip/label about how to disable plugins; In files: prefs.py:923; */
"Tip: You can disable a plugin by{CR}adding '{EXT}' to its folder name" = "Astuce : Vous pouvez désactiver un plugin en{CR}ajoutant '{EXT}' à son nom de répertoire";
/* prefs.py: Label on list of enabled plugins; In files: prefs.py:926; */
/* prefs.py: Label on list of enabled plugins; In files: prefs.py:934; */
"Enabled Plugins" = "Plugins activés";
/* prefs.py: Plugins - Label on URL to documentation about migrating plugins from Python 2.7; In files: prefs.py:954; */
/* prefs.py: Plugins - Label on URL to documentation about migrating plugins from Python 2.7; In files: prefs.py:962; */
"Information on migrating plugins" = "Informations sur la migration des plugins";
/* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:969; */
/* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:977; */
"Disabled Plugins" = "Plugins désactivés";
/* stats.py: Cmdr stats; In files: stats.py:60; */
/* stats.py: Cmdr stats; In files: stats.py:58; */
"Balance" = "Solde";
/* stats.py: Cmdr stats; In files: stats.py:61; */
/* stats.py: Cmdr stats; In files: stats.py:59; */
"Loan" = "Emprunt";
/* stats.py: Top rank; In files: stats.py:65; */
/* stats.py: Top rank; In files: stats.py:63; */
"Elite" = "Élite";
/* stats.py: Ranking; In files: stats.py:76; */
/* stats.py: Ranking; In files: stats.py:74; */
"Combat" = "Combattant";
/* stats.py: Ranking; In files: stats.py:77; */
/* stats.py: Ranking; In files: stats.py:75; */
"Trade" = "Commerçant";
/* stats.py: Ranking; In files: stats.py:78; */
/* stats.py: Ranking; In files: stats.py:76; */
"Explorer" = "Explorateur";
/* stats.py: Ranking; In files: stats.py:81; */
/* stats.py: Ranking; In files: stats.py:79; */
"CQC" = "CQC";
/* stats.py: Ranking; In files: stats.py:82; */
/* stats.py: Ranking; In files: stats.py:80; */
"Federation" = "Fédération";
/* stats.py: Ranking; In files: stats.py:83; */
/* stats.py: Ranking; In files: stats.py:81; */
"Empire" = "Empire";
/* stats.py: Ranking; In files: stats.py:84; */
/* stats.py: Ranking; In files: stats.py:82; */
"Powerplay" = "Powerplay";
/* stats.py: Combat rank; In files: stats.py:93; */
/* stats.py: Combat rank; In files: stats.py:91; */
"Harmless" = "Inoffensif";
/* stats.py: Combat rank; In files: stats.py:94; */
/* stats.py: Combat rank; In files: stats.py:92; */
"Mostly Harmless" = "Bleu";
/* stats.py: Combat rank; In files: stats.py:95; */
/* stats.py: Combat rank; In files: stats.py:93; */
"Novice" = "Novice";
/* stats.py: Combat rank; In files: stats.py:96; */
/* stats.py: Combat rank; In files: stats.py:94; */
"Competent" = "Compétent";
/* stats.py: Combat rank; In files: stats.py:97; */
/* stats.py: Combat rank; In files: stats.py:95; */
"Expert" = "Expert";
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:98; stats.py:178; */
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:96; stats.py:176; */
"Master" = "Vilain";
/* stats.py: Combat rank; In files: stats.py:99; */
/* stats.py: Combat rank; In files: stats.py:97; */
"Dangerous" = "Vétéran";
/* stats.py: Combat rank; In files: stats.py:100; */
/* stats.py: Combat rank; In files: stats.py:98; */
"Deadly" = "Létal";
/* stats.py: Trade rank; In files: stats.py:103; */
/* stats.py: Trade rank; In files: stats.py:101; */
"Penniless" = "Sans-le-sou";
/* stats.py: Trade rank; In files: stats.py:104; */
/* stats.py: Trade rank; In files: stats.py:102; */
"Mostly Penniless" = "Mendiant";
/* stats.py: Trade rank; In files: stats.py:105; */
/* stats.py: Trade rank; In files: stats.py:103; */
"Peddler" = "Boutiquier";
/* stats.py: Trade rank; In files: stats.py:106; */
/* stats.py: Trade rank; In files: stats.py:104; */
"Dealer" = "Revendeur";
/* stats.py: Trade rank; In files: stats.py:107; */
/* stats.py: Trade rank; In files: stats.py:105; */
"Merchant" = "Marchand";
/* stats.py: Trade rank; In files: stats.py:108; */
/* stats.py: Trade rank; In files: stats.py:106; */
"Broker" = "Courtier";
/* stats.py: Trade rank; In files: stats.py:109; */
/* stats.py: Trade rank; In files: stats.py:107; */
"Entrepreneur" = "Entrepreneur";
/* stats.py: Trade rank; In files: stats.py:110; */
/* stats.py: Trade rank; In files: stats.py:108; */
"Tycoon" = "Magnat";
/* stats.py: Explorer rank; In files: stats.py:113; */
/* stats.py: Explorer rank; In files: stats.py:111; */
"Aimless" = "Vagabond";
/* stats.py: Explorer rank; In files: stats.py:114; */
/* stats.py: Explorer rank; In files: stats.py:112; */
"Mostly Aimless" = "Touriste";
/* stats.py: Explorer rank; In files: stats.py:115; */
/* stats.py: Explorer rank; In files: stats.py:113; */
"Scout" = "Voyageur";
/* stats.py: Explorer rank; In files: stats.py:116; */
/* stats.py: Explorer rank; In files: stats.py:114; */
"Surveyor" = "Éclaireur";
/* stats.py: Explorer rank; In files: stats.py:117; */
/* stats.py: Explorer rank; In files: stats.py:115; */
"Trailblazer" = "Prospecteur";
/* stats.py: Explorer rank; In files: stats.py:118; */
/* stats.py: Explorer rank; In files: stats.py:116; */
"Pathfinder" = "Découvreur";
/* stats.py: Explorer rank; In files: stats.py:119; */
/* stats.py: Explorer rank; In files: stats.py:117; */
"Ranger" = "Navigateur";
/* stats.py: Explorer rank; In files: stats.py:120; */
/* stats.py: Explorer rank; In files: stats.py:118; */
"Pioneer" = "Pionnier";
/* stats.py: CQC rank; In files: stats.py:144; */
/* stats.py: CQC rank; In files: stats.py:142; */
"Helpless" = "Helpless";
/* stats.py: CQC rank; In files: stats.py:145; */
/* stats.py: CQC rank; In files: stats.py:143; */
"Mostly Helpless" = "Mostly Helpless";
/* stats.py: CQC rank; In files: stats.py:146; */
/* stats.py: CQC rank; In files: stats.py:144; */
"Amateur" = "Amateur";
/* stats.py: CQC rank; In files: stats.py:147; */
/* stats.py: CQC rank; In files: stats.py:145; */
"Semi Professional" = "Semi Professional";
/* stats.py: CQC rank; In files: stats.py:148; */
/* stats.py: CQC rank; In files: stats.py:146; */
"Professional" = "Professional";
/* stats.py: CQC rank; In files: stats.py:149; */
/* stats.py: CQC rank; In files: stats.py:147; */
"Champion" = "Champion";
/* stats.py: CQC rank; In files: stats.py:150; */
/* stats.py: CQC rank; In files: stats.py:148; */
"Hero" = "Hero";
/* stats.py: CQC rank; In files: stats.py:151; */
/* stats.py: CQC rank; In files: stats.py:149; */
"Gladiator" = "Gladiator";
/* stats.py: Federation rank; In files: stats.py:157; */
/* stats.py: Federation rank; In files: stats.py:155; */
"Recruit" = "Recrue";
/* stats.py: Federation rank; In files: stats.py:158; */
/* stats.py: Federation rank; In files: stats.py:156; */
"Cadet" = "Cadet";
/* stats.py: Federation rank; In files: stats.py:159; */
/* stats.py: Federation rank; In files: stats.py:157; */
"Midshipman" = "Matelot";
/* stats.py: Federation rank; In files: stats.py:160; */
/* stats.py: Federation rank; In files: stats.py:158; */
"Petty Officer" = "Second Maître";
/* stats.py: Federation rank; In files: stats.py:161; */
/* stats.py: Federation rank; In files: stats.py:159; */
"Chief Petty Officer" = "Premier Maître";
/* stats.py: Federation rank; In files: stats.py:162; */
/* stats.py: Federation rank; In files: stats.py:160; */
"Warrant Officer" = "Major";
/* stats.py: Federation rank; In files: stats.py:163; */
/* stats.py: Federation rank; In files: stats.py:161; */
"Ensign" = "Enseigne";
/* stats.py: Federation rank; In files: stats.py:164; */
/* stats.py: Federation rank; In files: stats.py:162; */
"Lieutenant" = "Lieutenant";
/* stats.py: Federation rank; In files: stats.py:165; */
/* stats.py: Federation rank; In files: stats.py:163; */
"Lieutenant Commander" = "Capitaine de Corvette";
/* stats.py: Federation rank; In files: stats.py:166; */
/* stats.py: Federation rank; In files: stats.py:164; */
"Post Commander" = "Capitaine de Frégate";
/* stats.py: Federation rank; In files: stats.py:167; */
/* stats.py: Federation rank; In files: stats.py:165; */
"Post Captain" = "Capitaine de Vaisseau";
/* stats.py: Federation rank; In files: stats.py:168; */
/* stats.py: Federation rank; In files: stats.py:166; */
"Rear Admiral" = "Contre-Amiral";
/* stats.py: Federation rank; In files: stats.py:169; */
/* stats.py: Federation rank; In files: stats.py:167; */
"Vice Admiral" = "Vice-Amiral";
/* stats.py: Federation rank; In files: stats.py:170; */
/* stats.py: Federation rank; In files: stats.py:168; */
"Admiral" = "Amiral";
/* stats.py: Empire rank; In files: stats.py:176; */
/* stats.py: Empire rank; In files: stats.py:174; */
"Outsider" = "Étranger";
/* stats.py: Empire rank; In files: stats.py:177; */
/* stats.py: Empire rank; In files: stats.py:175; */
"Serf" = "Serf";
/* stats.py: Empire rank; In files: stats.py:179; */
/* stats.py: Empire rank; In files: stats.py:177; */
"Squire" = "Écuyer";
/* stats.py: Empire rank; In files: stats.py:180; */
/* stats.py: Empire rank; In files: stats.py:178; */
"Knight" = "Chevalier";
/* stats.py: Empire rank; In files: stats.py:181; */
/* stats.py: Empire rank; In files: stats.py:179; */
"Lord" = "Banneret";
/* stats.py: Empire rank; In files: stats.py:182; */
/* stats.py: Empire rank; In files: stats.py:180; */
"Baron" = "Baron";
/* stats.py: Empire rank; In files: stats.py:183; */
/* stats.py: Empire rank; In files: stats.py:181; */
"Viscount" = "Vicomte";
/* stats.py: Empire rank; In files: stats.py:184; */
/* stats.py: Empire rank; In files: stats.py:182; */
"Count" = "Comte";
/* stats.py: Empire rank; In files: stats.py:185; */
/* stats.py: Empire rank; In files: stats.py:183; */
"Earl" = "Marquis";
/* stats.py: Empire rank; In files: stats.py:186; */
/* stats.py: Empire rank; In files: stats.py:184; */
"Marquis" = "Duc";
/* stats.py: Empire rank; In files: stats.py:187; */
/* stats.py: Empire rank; In files: stats.py:185; */
"Duke" = "Archiduc";
/* stats.py: Empire rank; In files: stats.py:188; */
/* stats.py: Empire rank; In files: stats.py:186; */
"Prince" = "Prince";
/* stats.py: Empire rank; In files: stats.py:189; */
/* stats.py: Empire rank; In files: stats.py:187; */
"King" = "Roi";
/* stats.py: Power rank; In files: stats.py:195; */
/* stats.py: Power rank; In files: stats.py:193; */
"Rating 1" = "Rang 1";
/* stats.py: Power rank; In files: stats.py:196; */
/* stats.py: Power rank; In files: stats.py:194; */
"Rating 2" = "Rang 2";
/* stats.py: Power rank; In files: stats.py:197; */
/* stats.py: Power rank; In files: stats.py:195; */
"Rating 3" = "Rang 3";
/* stats.py: Power rank; In files: stats.py:198; */
/* stats.py: Power rank; In files: stats.py:196; */
"Rating 4" = "Rang 4";
/* stats.py: Power rank; In files: stats.py:199; */
/* stats.py: Power rank; In files: stats.py:197; */
"Rating 5" = "Rang 5";
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:413; */
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:409; */
"Value" = "Valeur";
/* stats.py: Status dialog title; In files: stats.py:422; */
/* stats.py: Status dialog title; In files: stats.py:418; */
"Ships" = "Vaisseaux";
+273 -273
View File
@@ -1,503 +1,503 @@
/* Language name */
"!Language" = "Magyar";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:212; eddn.py:584; eddn.py:925; load.py:215; load.py:593; load.py:940; eddn.py:252; eddn.py:699; eddn.py:1439; */
"Sending data to EDDN..." = "Adatok küldése az EDDN-nek...";
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:257; eddn.py:864; eddn.py:898; eddn.py:937; load.py:260; load.py:878; load.py:913; load.py:952; eddn.py:316; eddn.py:1375; eddn.py:1410; eddn.py:1451; */
"Error: Can't connect to EDDN" = "Hiba:Nem lehet kapcsolódni az EDDN-hez";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:663; load.py:672; eddn.py:1122; */
"Send station data to the Elite Dangerous Data Network" = "Állomás adatok küldése az EDDN-nek";
/* eddn.py: Enable EDDN support for system and other scan data checkbox label; In files: eddn.py:673; load.py:682; eddn.py:1133; */
"Send system and scan data to the Elite Dangerous Data Network" = "Rendszer és szken adatok küldése az EDDN-nek";
/* eddn.py: EDDN delay sending until docked option is on, this message notes that a send was skipped due to this; In files: eddn.py:683; load.py:692; eddn.py:1144; */
"Delay sending until docked" = "Küldés késletetése amig dokkolsz";
/* edsm.py: Settings>EDSM - Label on checkbox for 'send data'; In files: edsm.py:197; load.py:197; edsm.py:237; */
"Send flight log and Cmdr status to EDSM" = "Repülési napló és Parancsnok státusz küldése az EDSM-nek";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:206; load.py:206; edsm.py:247; */
"Elite Dangerous Star Map credentials" = "Elite Dangerous Csillag térkép hitelesítése";
/* edsm.py: Game Commander name label in EDSM settings; theme.py: Label for commander name in main window; EDMarketConnector.py: Label for commander name in main window; stats.py: Cmdr stats; In files: edsm.py:216; load.py:216; edsm.py:258; theme.py:227; EDMarketConnector.py:822; stats.py:59; */
"Cmdr" = "Parancsnok";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:223; load.py:223; edsm.py:266; */
"Commander Name" = "Parancsnok név";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:230; inara.py:233; load.py:230; load.py:233; edsm.py:274; inara.py:243; */
"API Key" = "API kulcs";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:256; load.py:256; edsm.py:301; prefs.py:520; prefs.py:1174; prefs.py:1207; stats.py:156; stats.py:175; stats.py:194; stats.py:211; */
"None" = "Egyik sem";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:622; edsm.py:724; load.py:627; load.py:729; edsm.py:747; edsm.py:875; */
"Error: EDSM {MSG}" = "Hiba:EDSM {MSG}";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:658; edsm.py:720; load.py:663; load.py:725; edsm.py:784; edsm.py:870; */
"Error: Can't connect to EDSM" = "Hiba:nem lehet kapcsólodni az EDSM-hez";
/* inara.py: Checkbox to enable INARA API Usage; In files: inara.py:215; load.py:215; inara.py:222; */
"Send flight log and Cmdr status to Inara" = "Repülési napló és Parancsnok státusz küldése az Inarának";
/* inara.py: Text for INARA API keys link ( goes to https://inara.cz/settings-api ); In files: inara.py:225; load.py:225; inara.py:234; */
"Inara credentials" = "Inara hitelesitő adatok";
/* inara.py: INARA API returned some kind of error (error message will be contained in {MSG}); In files: inara.py:1316; inara.py:1328; load.py:1319; load.py:1331; inara.py:1587; inara.py:1600; */
"Error: Inara {MSG}" = "Hiba:Inara {MSG}";
/* ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; In files: ttkHyperlinkLabel.py:42; EDMarketConnector.py:866; */
"Copy" = "Másol";
/* EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:529; EDMarketConnector.py:828; EDMarketConnector.py:1520; */
"Update" = "Frissítés";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:612; prefs.py:867; */
"Always on top" = "Mindig felül";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:824; EDMarketConnector.py:1279; */
"Role" = "Szerep";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:824; EDMarketConnector.py:1289; EDMarketConnector.py:1312; stats.py:409; */
"Ship" = "Hajó";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:826; prefs.py:607; stats.py:411; */
"System" = "Rendszer";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:827; prefs.py:625; prefs.py:762; stats.py:412; */
"Station" = "Állomás";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:830; EDMarketConnector.py:845; EDMarketConnector.py:848; EDMarketConnector.py:2019; */
"File" = "Fájl";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:831; EDMarketConnector.py:846; EDMarketConnector.py:849; */
"Edit" = "Szerkeszt";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:832; */
"View" = "Nézet";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:833; */
"Window" = "Ablak";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:834; EDMarketConnector.py:847; EDMarketConnector.py:850; */
"Help" = "Segítség";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:837; EDMarketConnector.py:863; EDMarketConnector.py:1566; */
"About {APP}" = "Az {APP} névjegye";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:839; EDMarketConnector.py:862; */
"Check for Updates..." = "Frissítések keresése";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:840; EDMarketConnector.py:854; */
"Save Raw Data..." = "Nyers adat elmentése";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:841; EDMarketConnector.py:853; stats.py:406; */
"Status" = "Státusz";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:842; EDMarketConnector.py:860; */
"Privacy Policy" = "Adatvédelmi irányelvek";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:843; EDMarketConnector.py:861; EDMarketConnector.py:1600; */
"Release Notes" = "Kiadási megjegyzések";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:855; EDMarketConnector.py:2020; prefs.py:255; */
"Settings" = "Beállítások";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:856; */
"Exit" = "Kilépés";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:859; */
"Documentation" = "Dokumentáció";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:872; */
"Logging in..." = "Bejelentkezés";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:888; EDMarketConnector.py:1431; */
"Authentication successful" = "A hitelesítés sikerült";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:919; */
"You're not docked at a station!" = " Nem dokkoltál az állomáson";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:927; */
"Station doesn't have anything!" = "Az állomáson nincs semmi!";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:932; */
"Station doesn't have a market!" = "Nincs piac az állomáson!";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1006; */
"Fetching data..." = "Adatok lekérése";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1051; stats.py:335; */
"Who are you?!" = "Ki vagy?";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1057; stats.py:345; */
"Where are you?!" = "Hol vagy?";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1064; stats.py:353; */
"What are you flying?!" = "Mivel repülsz?";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1226; */
"Last updated at %H:%M:%S" = "Utoljára frissítve %H:%M:%S";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1254; */
"Fighter" = "Vadászgép";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1255; */
"Gunner" = "Lövész";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1256; */
"Helm" = "Kormányos";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1514; */
"cooldown {SS}s" = "Lehűl {SS} másodperc";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1626; prefs.py:306; */
"OK" = "Ok";
/* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2018; prefs.py:978; */
"Plugins" = "Pluginok";
/* companion.py: Frontier CAPI data doesn't agree with latest Journal game location; In files: companion.py:237; */
/* companion.py: Frontier CAPI data doesn't agree with latest Journal game location; In files: companion.py:245; */
"Error: Frontier server is lagging" = "Hiba:Frontier szerver laggol";
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:263; */
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:271; */
"Error: Invalid Credentials" = "Hiba:Érvénytelen hitelesítő adatok";
/* companion.py: Frontier CAPI authorisation not for currently game-active commander; In files: companion.py:288; */
/* companion.py: Frontier CAPI authorisation not for currently game-active commander; In files: companion.py:296; */
"Error: Wrong Cmdr" = "Hiba:Rossz parancsnok";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:194; prefs.py:468; prefs.py:702; prefs.py:735; */
/* EDMarketConnector.py: Main UI Update button; EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:601; EDMarketConnector.py:919; EDMarketConnector.py:1748; */
"Update" = "Frissítés";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:710; prefs.py:875; */
"Always on top" = "Mindig felül";
/* EDMarketConnector.py: Label for commander name in main window; edsm.py: Game Commander name label in EDSM settings; stats.py: Cmdr stats; theme.py: Label for commander name in main window; In files: EDMarketConnector.py:913; edsm.py:332; stats.py:57; theme.py:290; */
"Cmdr" = "Parancsnok";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:915; EDMarketConnector.py:1487; */
"Role" = "Szerep";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:915; EDMarketConnector.py:1497; EDMarketConnector.py:1520; stats.py:405; */
"Ship" = "Hajó";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:917; prefs.py:614; stats.py:407; */
"System" = "Rendszer";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:918; prefs.py:632; prefs.py:770; stats.py:408; */
"Station" = "Állomás";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:921; EDMarketConnector.py:939; EDMarketConnector.py:942; EDMarketConnector.py:2264; */
"File" = "Fájl";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:922; EDMarketConnector.py:940; EDMarketConnector.py:943; */
"Edit" = "Szerkeszt";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:923; */
"View" = "Nézet";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:924; */
"Window" = "Ablak";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:925; EDMarketConnector.py:941; EDMarketConnector.py:944; */
"Help" = "Segítség";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:928; EDMarketConnector.py:959; EDMarketConnector.py:1804; */
"About {APP}" = "Az {APP} névjegye";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:930; EDMarketConnector.py:958; */
"Check for Updates..." = "Frissítések keresése";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:931; EDMarketConnector.py:948; */
"Save Raw Data..." = "Nyers adat elmentése";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:932; EDMarketConnector.py:947; stats.py:402; */
"Status" = "Státusz";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:933; EDMarketConnector.py:953; */
"Documentation" = "Dokumentáció";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:936; EDMarketConnector.py:956; */
"Privacy Policy" = "Adatvédelmi irányelvek";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:937; EDMarketConnector.py:957; EDMarketConnector.py:1838; */
"Release Notes" = "Kiadási megjegyzések";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:949; EDMarketConnector.py:2265; prefs.py:241; */
"Settings" = "Beállítások";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:950; */
"Exit" = "Kilépés";
/* EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; In files: EDMarketConnector.py:962; ttkHyperlinkLabel.py:53; */
"Copy" = "Másol";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:978; */
"Logging in..." = "Bejelentkezés";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:994; EDMarketConnector.py:1657; */
"Authentication successful" = "A hitelesítés sikerült";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:1025; */
"You're not docked at a station!" = " Nem dokkoltál az állomáson";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:1033; */
"Station doesn't have anything!" = "Az állomáson nincs semmi!";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:1038; */
"Station doesn't have a market!" = "Nincs piac az állomáson!";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1128; EDMarketConnector.py:1179; */
"Fetching data..." = "Adatok lekérése";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1246; stats.py:333; */
"Who are you?!" = "Ki vagy?";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1252; stats.py:341; */
"Where are you?!" = "Hol vagy?";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1259; stats.py:349; */
"What are you flying?!" = "Mivel repülsz?";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1434; */
"Last updated at %H:%M:%S" = "Utoljára frissítve %H:%M:%S";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1462; */
"Fighter" = "Vadászgép";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1463; */
"Gunner" = "Lövész";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1464; */
"Helm" = "Kormányos";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1742; */
"cooldown {SS}s" = "Lehűl {SS} másodperc";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1864; prefs.py:292; */
"OK" = "Ok";
/* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2263; prefs.py:986; */
"Plugins" = "Pluginok";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:193; prefs.py:455; prefs.py:709; prefs.py:742; */
"Default" = "Alap";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:251; */
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:458; eddn.py:2413; eddn.py:2451; eddn.py:2519; */
"Error: Can't connect to EDDN" = "Hiba:Nem lehet kapcsolódni az EDDN-hez";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:2041; */
"Send station data to the Elite Dangerous Data Network" = "Állomás adatok küldése az EDDN-nek";
/* eddn.py: Enable EDDN support for system and other scan data checkbox label; In files: eddn.py:2052; */
"Send system and scan data to the Elite Dangerous Data Network" = "Rendszer és szken adatok küldése az EDDN-nek";
/* eddn.py: EDDN delay sending until docked option is on, this message notes that a send was skipped due to this; In files: eddn.py:2063; */
"Delay sending until docked" = "Küldés késletetése amig dokkolsz";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:2507; */
"Sending data to EDDN..." = "Adatok küldése az EDDN-nek...";
/* edsm.py: Settings>EDSM - Label on checkbox for 'send data'; In files: edsm.py:308; */
"Send flight log and Cmdr status to EDSM" = "Repülési napló és Parancsnok státusz küldése az EDSM-nek";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:319; */
"Elite Dangerous Star Map credentials" = "Elite Dangerous Csillag térkép hitelesítése";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:341; */
"Commander Name" = "Parancsnok név";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:350; inara.py:278; */
"API Key" = "API kulcs";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:394; prefs.py:527; prefs.py:1157; prefs.py:1190; stats.py:154; stats.py:173; stats.py:192; stats.py:209; */
"None" = "Egyik sem";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:916; edsm.py:1048; */
"Error: EDSM {MSG}" = "Hiba:EDSM {MSG}";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:953; edsm.py:1043; */
"Error: Can't connect to EDSM" = "Hiba:nem lehet kapcsólodni az EDSM-hez";
/* inara.py: Checkbox to enable INARA API Usage; In files: inara.py:257; */
"Send flight log and Cmdr status to Inara" = "Repülési napló és Parancsnok státusz küldése az Inarának";
/* inara.py: Text for INARA API keys link ( goes to https://inara.cz/settings-api ); In files: inara.py:269; */
"Inara credentials" = "Inara hitelesitő adatok";
/* inara.py: INARA API returned some kind of error (error message will be contained in {MSG}); In files: inara.py:1650; inara.py:1663; */
"Error: Inara {MSG}" = "Hiba:Inara {MSG}";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:237; */
"Preferences" = "Beállítások";
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:348; */
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:335; */
"Please choose what data to save" = "Kérem válassza ki a menteni kivánt adatokat.";
/* prefs.py: Settings > Output option; In files: prefs.py:354; */
/* prefs.py: Settings > Output option; In files: prefs.py:341; */
"Market data in CSV format file" = "Piaci adat CSV fájlban";
/* prefs.py: Settings > Output option; In files: prefs.py:363; */
/* prefs.py: Settings > Output option; In files: prefs.py:350; */
"Market data in Trade Dangerous format file" = "Piaci adat Trade Dangerous fájlban";
/* prefs.py: Settings > Output option; In files: prefs.py:373; */
/* prefs.py: Settings > Output option; In files: prefs.py:360; */
"Ship loadout" = "Hajó felszerelése";
/* prefs.py: Settings > Output option; In files: prefs.py:383; */
/* prefs.py: Settings > Output option; In files: prefs.py:370; */
"Automatically update on docking" = "Automatikusan frissit dokkolásnál";
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:392; prefs.py:411; */
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:379; prefs.py:398; */
"File location" = "Fálj helye";
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:400; prefs.py:450; */
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:387; prefs.py:437; */
"Change..." = "Módosítás...";
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:403; prefs.py:453; */
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:390; prefs.py:440; */
"Browse..." = "Böngész";
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:418; */
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:405; */
"Output" = "Kimenet";
/* prefs.py: Settings > Configuration - Label for Journal files location; In files: prefs.py:444; prefs.py:459; */
/* prefs.py: Settings > Configuration - Label for Journal files location; In files: prefs.py:431; prefs.py:446; */
"E:D journal file location" = "E:D journal fájl helye";
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:484; */
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:490; */
"Keyboard shortcut" = "Billentyűparancsok";
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:486; */
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:492; */
"Hotkey" = "Gyorsgomb";
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:495; */
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:501; */
"Re-start {APP} to use shortcuts" = "Újranidítás {APP} parancsikonok használatával";
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:504; */
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:510; */
"{APP} needs permission to use shortcuts" = "{APP} engedély szükséges a parancsikon használatához";
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:509; */
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:515; */
"Open System Preferences" = "Rendszerbeállítások megnyitáss";
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:531; */
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:538; */
"Only when Elite: Dangerous is the active app" = "Csak ha az Elite Dangerous fut";
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:542; */
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:549; */
"Play sound" = "Zene be";
/* prefs.py: Label for preferred shipyard, system and station 'providers'; In files: prefs.py:570; */
/* prefs.py: Label for preferred shipyard, system and station 'providers'; In files: prefs.py:577; */
"Preferred websites" = "Preferált weboldalak";
/* prefs.py: Label for Shipyard provider selection; In files: prefs.py:581; */
/* prefs.py: Label for Shipyard provider selection; In files: prefs.py:588; */
"Shipyard" = "Hajógyár";
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:674; */
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:681; */
"Configuration" = "Konfiguráció";
/* prefs.py: Preferences privacy tab title; In files: prefs.py:696; */
/* prefs.py: Preferences privacy tab title; In files: prefs.py:703; */
"Privacy" = "Titkosítás";
/* prefs.py: Label for Settings > Appeareance > selection of 'normal' text colour; In files: prefs.py:709; */
/* prefs.py: Label for Settings > Appeareance > selection of 'normal' text colour; In files: prefs.py:716; */
"Normal text" = "Normál txt";
/* prefs.py: Label for Settings > Appeareance > selection of 'highlightes' text colour; In files: prefs.py:711; */
/* prefs.py: Label for Settings > Appeareance > selection of 'highlightes' text colour; In files: prefs.py:718; */
"Highlighted text" = "Kiemelt szöveg";
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:720; */
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:727; */
"Language" = "Nyelv";
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:730; */
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:737; */
"Theme" = "Téma";
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:741; */
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:749; */
"Dark" = "Sötét";
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:748; */
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:756; */
"Transparent" = "Átlárható";
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:885; */
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:893; */
"Appearance" = "Megjelenés";
/* prefs.py: Label for location of third-party plugins folder; In files: prefs.py:900; */
/* prefs.py: Label for location of third-party plugins folder; In files: prefs.py:908; */
"Plugins folder" = "Plugin mappa";
/* prefs.py: Label on button used to open a filesystem folder; In files: prefs.py:907; */
/* prefs.py: Label on button used to open a filesystem folder; In files: prefs.py:915; */
"Open" = "Nyisd ki";
/* prefs.py: Tip/label about how to disable plugins; In files: prefs.py:915; */
/* prefs.py: Tip/label about how to disable plugins; In files: prefs.py:923; */
"Tip: You can disable a plugin by{CR}adding '{EXT}' to its folder name" = "Tipp:a pluginokat kikapcsolhatod hozzà{CR}adsz a mappa nevében egy '{EXT}'";
/* prefs.py: Label on list of enabled plugins; In files: prefs.py:926; */
/* prefs.py: Label on list of enabled plugins; In files: prefs.py:934; */
"Enabled Plugins" = "Pluginok engedélyezése";
/* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:969; */
/* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:977; */
"Disabled Plugins" = "Plugin kikapcsolása";
/* stats.py: Cmdr stats; In files: stats.py:60; */
/* stats.py: Cmdr stats; In files: stats.py:58; */
"Balance" = "Egyenleg";
/* stats.py: Cmdr stats; In files: stats.py:61; */
/* stats.py: Cmdr stats; In files: stats.py:59; */
"Loan" = "Kölcsön";
/* stats.py: Top rank; In files: stats.py:65; */
/* stats.py: Top rank; In files: stats.py:63; */
"Elite" = "Elit";
/* stats.py: Ranking; In files: stats.py:76; */
/* stats.py: Ranking; In files: stats.py:74; */
"Combat" = "Harc";
/* stats.py: Ranking; In files: stats.py:77; */
/* stats.py: Ranking; In files: stats.py:75; */
"Trade" = "Kereskedés";
/* stats.py: Ranking; In files: stats.py:78; */
/* stats.py: Ranking; In files: stats.py:76; */
"Explorer" = "Felfedező";
/* stats.py: Ranking; In files: stats.py:81; */
/* stats.py: Ranking; In files: stats.py:79; */
"CQC" = "CQC";
/* stats.py: Ranking; In files: stats.py:82; */
/* stats.py: Ranking; In files: stats.py:80; */
"Federation" = "Föderáció";
/* stats.py: Ranking; In files: stats.py:83; */
/* stats.py: Ranking; In files: stats.py:81; */
"Empire" = "Birodalom";
/* stats.py: Ranking; In files: stats.py:84; */
/* stats.py: Ranking; In files: stats.py:82; */
"Powerplay" = "Powerplay";
/* stats.py: Combat rank; In files: stats.py:93; */
/* stats.py: Combat rank; In files: stats.py:91; */
"Harmless" = "ártalmatlan";
/* stats.py: Combat rank; In files: stats.py:94; */
/* stats.py: Combat rank; In files: stats.py:92; */
"Mostly Harmless" = "Többnyire ártalmatlan";
/* stats.py: Combat rank; In files: stats.py:95; */
/* stats.py: Combat rank; In files: stats.py:93; */
"Novice" = "Kezdő";
/* stats.py: Combat rank; In files: stats.py:96; */
/* stats.py: Combat rank; In files: stats.py:94; */
"Competent" = "Képesített";
/* stats.py: Combat rank; In files: stats.py:97; */
/* stats.py: Combat rank; In files: stats.py:95; */
"Expert" = "Szakértő";
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:98; stats.py:178; */
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:96; stats.py:176; */
"Master" = "Mester";
/* stats.py: Combat rank; In files: stats.py:99; */
/* stats.py: Combat rank; In files: stats.py:97; */
"Dangerous" = "Veszélyes";
/* stats.py: Combat rank; In files: stats.py:100; */
/* stats.py: Combat rank; In files: stats.py:98; */
"Deadly" = "Halálos";
/* stats.py: Trade rank; In files: stats.py:103; */
/* stats.py: Trade rank; In files: stats.py:101; */
"Penniless" = "Nincstelen";
/* stats.py: Trade rank; In files: stats.py:104; */
/* stats.py: Trade rank; In files: stats.py:102; */
"Mostly Penniless" = "Többnyire nincstelen";
/* stats.py: Trade rank; In files: stats.py:105; */
/* stats.py: Trade rank; In files: stats.py:103; */
"Peddler" = "Házaló ügynők";
/* stats.py: Trade rank; In files: stats.py:106; */
/* stats.py: Trade rank; In files: stats.py:104; */
"Dealer" = "Kereskedő";
/* stats.py: Trade rank; In files: stats.py:107; */
/* stats.py: Trade rank; In files: stats.py:105; */
"Merchant" = "Kereskedő";
/* stats.py: Trade rank; In files: stats.py:108; */
/* stats.py: Trade rank; In files: stats.py:106; */
"Broker" = "Bróker";
/* stats.py: Trade rank; In files: stats.py:109; */
/* stats.py: Trade rank; In files: stats.py:107; */
"Entrepreneur" = "Vállalkozó";
/* stats.py: Trade rank; In files: stats.py:110; */
/* stats.py: Trade rank; In files: stats.py:108; */
"Tycoon" = "Iparmágnás";
/* stats.py: Explorer rank; In files: stats.py:113; */
/* stats.py: Explorer rank; In files: stats.py:111; */
"Aimless" = "Céltalan";
/* stats.py: Explorer rank; In files: stats.py:114; */
/* stats.py: Explorer rank; In files: stats.py:112; */
"Mostly Aimless" = "Tøbnyire céltalan";
/* stats.py: Explorer rank; In files: stats.py:115; */
/* stats.py: Explorer rank; In files: stats.py:113; */
"Scout" = "Felderítő";
/* stats.py: Explorer rank; In files: stats.py:116; */
/* stats.py: Explorer rank; In files: stats.py:114; */
"Surveyor" = "Megfigyelő";
/* stats.py: Explorer rank; In files: stats.py:117; */
/* stats.py: Explorer rank; In files: stats.py:115; */
"Trailblazer" = "Útkereső";
/* stats.py: Explorer rank; In files: stats.py:118; */
/* stats.py: Explorer rank; In files: stats.py:116; */
"Pathfinder" = "Útkereső";
/* stats.py: Explorer rank; In files: stats.py:119; */
/* stats.py: Explorer rank; In files: stats.py:117; */
"Ranger" = "Vándor";
/* stats.py: Explorer rank; In files: stats.py:120; */
/* stats.py: Explorer rank; In files: stats.py:118; */
"Pioneer" = "Utász";
/* stats.py: CQC rank; In files: stats.py:144; */
/* stats.py: CQC rank; In files: stats.py:142; */
"Helpless" = "tehetetlen";
/* stats.py: CQC rank; In files: stats.py:145; */
/* stats.py: CQC rank; In files: stats.py:143; */
"Mostly Helpless" = "Többnyire tehetetlen";
/* stats.py: CQC rank; In files: stats.py:146; */
/* stats.py: CQC rank; In files: stats.py:144; */
"Amateur" = "Amatőr";
/* stats.py: CQC rank; In files: stats.py:147; */
/* stats.py: CQC rank; In files: stats.py:145; */
"Semi Professional" = "Fél profi";
/* stats.py: CQC rank; In files: stats.py:148; */
/* stats.py: CQC rank; In files: stats.py:146; */
"Professional" = "Szakember";
/* stats.py: CQC rank; In files: stats.py:149; */
/* stats.py: CQC rank; In files: stats.py:147; */
"Champion" = "Bajnok";
/* stats.py: CQC rank; In files: stats.py:150; */
/* stats.py: CQC rank; In files: stats.py:148; */
"Hero" = "Hős";
/* stats.py: CQC rank; In files: stats.py:151; */
/* stats.py: CQC rank; In files: stats.py:149; */
"Gladiator" = "Gladiátor";
/* stats.py: Federation rank; In files: stats.py:157; */
/* stats.py: Federation rank; In files: stats.py:155; */
"Recruit" = "Újonc";
/* stats.py: Federation rank; In files: stats.py:158; */
/* stats.py: Federation rank; In files: stats.py:156; */
"Cadet" = "Kadét";
/* stats.py: Federation rank; In files: stats.py:159; */
/* stats.py: Federation rank; In files: stats.py:157; */
"Midshipman" = "Tengerészkadét";
/* stats.py: Federation rank; In files: stats.py:160; */
/* stats.py: Federation rank; In files: stats.py:158; */
"Petty Officer" = "Tizedes";
/* stats.py: Federation rank; In files: stats.py:161; */
/* stats.py: Federation rank; In files: stats.py:159; */
"Chief Petty Officer" = "Törzs zászlós";
/* stats.py: Federation rank; In files: stats.py:162; */
/* stats.py: Federation rank; In files: stats.py:160; */
"Warrant Officer" = "Tiszthelyettes";
/* stats.py: Federation rank; In files: stats.py:163; */
/* stats.py: Federation rank; In files: stats.py:161; */
"Ensign" = "Zászlós";
/* stats.py: Federation rank; In files: stats.py:164; */
/* stats.py: Federation rank; In files: stats.py:162; */
"Lieutenant" = "Hadnagy";
/* stats.py: Federation rank; In files: stats.py:165; */
/* stats.py: Federation rank; In files: stats.py:163; */
"Lieutenant Commander" = "Főhadnagy";
/* stats.py: Federation rank; In files: stats.py:166; */
/* stats.py: Federation rank; In files: stats.py:164; */
"Post Commander" = "Ezredes";
/* stats.py: Federation rank; In files: stats.py:167; */
/* stats.py: Federation rank; In files: stats.py:165; */
"Post Captain" = "Ellentengernagy";
/* stats.py: Federation rank; In files: stats.py:168; */
/* stats.py: Federation rank; In files: stats.py:166; */
"Rear Admiral" = "FőEllentengernagy";
/* stats.py: Federation rank; In files: stats.py:169; */
/* stats.py: Federation rank; In files: stats.py:167; */
"Vice Admiral" = "Altengernagy";
/* stats.py: Federation rank; In files: stats.py:170; */
/* stats.py: Federation rank; In files: stats.py:168; */
"Admiral" = "Admirális";
/* stats.py: Empire rank; In files: stats.py:176; */
/* stats.py: Empire rank; In files: stats.py:174; */
"Outsider" = "Kivűlálló";
/* stats.py: Empire rank; In files: stats.py:177; */
/* stats.py: Empire rank; In files: stats.py:175; */
"Serf" = "Jobbágy";
/* stats.py: Empire rank; In files: stats.py:179; */
/* stats.py: Empire rank; In files: stats.py:177; */
"Squire" = "Földesúr";
/* stats.py: Empire rank; In files: stats.py:180; */
/* stats.py: Empire rank; In files: stats.py:178; */
"Knight" = "Lovag";
/* stats.py: Empire rank; In files: stats.py:181; */
/* stats.py: Empire rank; In files: stats.py:179; */
"Lord" = "Lord";
/* stats.py: Empire rank; In files: stats.py:182; */
/* stats.py: Empire rank; In files: stats.py:180; */
"Baron" = "Báró";
/* stats.py: Empire rank; In files: stats.py:183; */
/* stats.py: Empire rank; In files: stats.py:181; */
"Viscount" = "Vicomte";
/* stats.py: Empire rank; In files: stats.py:184; */
/* stats.py: Empire rank; In files: stats.py:182; */
"Count" = "Számol";
/* stats.py: Empire rank; In files: stats.py:185; */
/* stats.py: Empire rank; In files: stats.py:183; */
"Earl" = "Gróf";
/* stats.py: Empire rank; In files: stats.py:186; */
/* stats.py: Empire rank; In files: stats.py:184; */
"Marquis" = "Őrgróf";
/* stats.py: Empire rank; In files: stats.py:187; */
/* stats.py: Empire rank; In files: stats.py:185; */
"Duke" = "Herceg";
/* stats.py: Empire rank; In files: stats.py:188; */
/* stats.py: Empire rank; In files: stats.py:186; */
"Prince" = "Herceg";
/* stats.py: Empire rank; In files: stats.py:189; */
/* stats.py: Empire rank; In files: stats.py:187; */
"King" = "Király";
/* stats.py: Power rank; In files: stats.py:195; */
/* stats.py: Power rank; In files: stats.py:193; */
"Rating 1" = "Osztákgozás 1";
/* stats.py: Power rank; In files: stats.py:196; */
/* stats.py: Power rank; In files: stats.py:194; */
"Rating 2" = "Osztályozás 2";
/* stats.py: Power rank; In files: stats.py:197; */
/* stats.py: Power rank; In files: stats.py:195; */
"Rating 3" = "Osztályozás 3";
/* stats.py: Power rank; In files: stats.py:198; */
/* stats.py: Power rank; In files: stats.py:196; */
"Rating 4" = "Osztályozás 4";
/* stats.py: Power rank; In files: stats.py:199; */
/* stats.py: Power rank; In files: stats.py:197; */
"Rating 5" = "Osztályozás 5";
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:413; */
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:409; */
"Value" = "Érték";
/* stats.py: Status dialog title; In files: stats.py:422; */
/* stats.py: Status dialog title; In files: stats.py:418; */
"Ships" = "Hajók";
+344 -344
View File
@@ -1,596 +1,596 @@
/* Language name */
"!Language" = "Italiano";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:212; eddn.py:584; eddn.py:925; load.py:215; load.py:593; load.py:940; eddn.py:252; eddn.py:699; eddn.py:1439; */
"Sending data to EDDN..." = "Sto inviando i dati a EDDN...";
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:257; eddn.py:864; eddn.py:898; eddn.py:937; load.py:260; load.py:878; load.py:913; load.py:952; eddn.py:316; eddn.py:1375; eddn.py:1410; eddn.py:1451; */
"Error: Can't connect to EDDN" = "Errore: Non riesco a connettermi a EDDN";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:663; load.py:672; eddn.py:1122; */
"Send station data to the Elite Dangerous Data Network" = "Manda i dati della stazione a Elite Dangerous Data Network";
/* eddn.py: Enable EDDN support for system and other scan data checkbox label; In files: eddn.py:673; load.py:682; eddn.py:1133; */
"Send system and scan data to the Elite Dangerous Data Network" = "Manda il sistema e i dati scansionati a EDDN";
/* eddn.py: EDDN delay sending until docked option is on, this message notes that a send was skipped due to this; In files: eddn.py:683; load.py:692; eddn.py:1144; */
"Delay sending until docked" = "Ritarda l'invio fino a che si è ormeggiati";
/* edsm.py: Settings>EDSM - Label on checkbox for 'send data'; In files: edsm.py:197; load.py:197; edsm.py:237; */
"Send flight log and Cmdr status to EDSM" = "Manda il flight log e lo stato Cmdr a EDSM";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:206; load.py:206; edsm.py:247; */
"Elite Dangerous Star Map credentials" = "Credenziali per Elite Dangerous Star Map";
/* edsm.py: Game Commander name label in EDSM settings; theme.py: Label for commander name in main window; EDMarketConnector.py: Label for commander name in main window; stats.py: Cmdr stats; In files: edsm.py:216; load.py:216; edsm.py:258; theme.py:227; EDMarketConnector.py:822; stats.py:59; */
"Cmdr" = "Cmdr";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:223; load.py:223; edsm.py:266; */
"Commander Name" = "Nome Commandante";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:230; inara.py:233; load.py:230; load.py:233; edsm.py:274; inara.py:243; */
"API Key" = "API Key";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:256; load.py:256; edsm.py:301; prefs.py:520; prefs.py:1174; prefs.py:1207; stats.py:156; stats.py:175; stats.py:194; stats.py:211; */
"None" = "Nessuna";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:622; edsm.py:724; load.py:627; load.py:729; edsm.py:747; edsm.py:875; */
"Error: EDSM {MSG}" = "Errore: EDSM {MSG}";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:658; edsm.py:720; load.py:663; load.py:725; edsm.py:784; edsm.py:870; */
"Error: Can't connect to EDSM" = "Errore: Non riesco a connettermi a EDSM";
/* inara.py: Checkbox to enable INARA API Usage; In files: inara.py:215; load.py:215; inara.py:222; */
"Send flight log and Cmdr status to Inara" = "Manda il flight log e lo stato Cmdr a Inara";
/* inara.py: Text for INARA API keys link ( goes to https://inara.cz/settings-api ); In files: inara.py:225; load.py:225; inara.py:234; */
"Inara credentials" = "Credenziali per Inara";
/* inara.py: INARA API returned some kind of error (error message will be contained in {MSG}); In files: inara.py:1316; inara.py:1328; load.py:1319; load.py:1331; inara.py:1587; inara.py:1600; */
"Error: Inara {MSG}" = "Errore: Inara {MSG}";
/* coriolis.py: 'Auto' label for Coriolis site override selection; coriolis.py: Coriolis normal/beta selection - auto; In files: load.py:51; load.py:51; load.py:88; load.py:104; load.py:110; coriolis.py:52; coriolis.py:55; coriolis.py:101; coriolis.py:117; coriolis.py:123; */
"Auto" = "Auto";
/* coriolis.py: 'Normal' label for Coriolis site override selection; coriolis.py: Coriolis normal/beta selection - normal; In files: load.py:51; load.py:88; load.py:102; coriolis.py:53; coriolis.py:99; coriolis.py:115; */
"Normal" = "Normale";
/* coriolis.py: 'Beta' label for Coriolis site override selection; coriolis.py: Coriolis normal/beta selection - beta; In files: load.py:51; load.py:88; load.py:103; coriolis.py:54; coriolis.py:100; coriolis.py:116; */
"Beta" = "Beta";
/* coriolis.py: Settings>Coriolis: Label for 'NOT alpha/beta game version' URL; In files: load.py:69; coriolis.py:75; */
"Normal URL" = "URL normale";
/* coriolis.py: Generic 'Reset' button label; In files: load.py:71; load.py:78; coriolis.py:78; coriolis.py:87; */
"Reset" = "Reimposta";
/* coriolis.py: Settings>Coriolis: Label for 'alpha/beta game version' URL; In files: load.py:76; coriolis.py:84; */
"Beta URL" = "URL Beta";
/* eddb.py: Journal Processing disabled due to an active killswitch; In files: load.py:99; eddb.py:100; */
"EDDB Journal processing disabled. See Log." = "Elaborazione di EDDB Journal disabilitata. Guarda il Log.";
/* inara.py: INARA support disabled via killswitch; In files: load.py:331; inara.py:341; */
"Inara disabled. See Log." = "Inara disabilitato. Vedi il Log.";
/* EDMarketConnector.py: No data was returned for the commander from the Frontier CAPI; In files: test_ast_stuff.py:2; EDMarketConnector.py:1047; */
"CAPI: No commander data returned" = "CAPI: Nessun dato sul commandante";
/* coriolis.py: Settings>Coriolis: Label for selection of using Normal, Beta or 'auto' Coriolis URL; In files: coriolis.py:94; */
"Override Beta/Normal Selection" = "Sovrascrivi selezione Beta/Normale";
/* journal_lock.py: Title text on popup when Journal directory already locked; In files: journal_lock.py:206; */
"Journal directory already locked" = "cartella Journal già bloccata";
/* journal_lock.py: Text for when newly selected Journal directory is already locked; In files: journal_lock.py:223:224; */
"The new Journal Directory location is already locked.{CR}You can either attempt to resolve this and then Retry, or choose to Ignore this." = "La nuova cartella dove risiede il Journal è già bloccata.{CR}Puoi provare a risolvere Riprovando, oppure scegliere di ignorare la cosa.";
/* journal_lock.py: Generic 'Retry' button label; In files: journal_lock.py:228; */
"Retry" = "Riprova";
/* journal_lock.py: Generic 'Ignore' button label; In files: journal_lock.py:232; */
"Ignore" = "Ignora";
/* ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; In files: ttkHyperlinkLabel.py:42; EDMarketConnector.py:866; */
"Copy" = "Copia";
/* EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:529; EDMarketConnector.py:828; EDMarketConnector.py:1520; */
"Update" = "Recupera";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:612; prefs.py:867; */
"Always on top" = "Sempre in primo piano";
/* EDMarketConnector.py: Unknown suit; In files: EDMarketConnector.py:746; */
"Unknown" = "Sconosciuto";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:824; EDMarketConnector.py:1279; */
"Role" = "Ruolo";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:824; EDMarketConnector.py:1289; EDMarketConnector.py:1312; stats.py:409; */
"Ship" = "Nave";
/* EDMarketConnector.py: Label for 'Suit' line in main UI; In files: EDMarketConnector.py:825; */
"Suit" = "Tuta";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:826; prefs.py:607; stats.py:411; */
"System" = "Sistema";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:827; prefs.py:625; prefs.py:762; stats.py:412; */
"Station" = "Stazione";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:830; EDMarketConnector.py:845; EDMarketConnector.py:848; EDMarketConnector.py:2019; */
"File" = "File";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:831; EDMarketConnector.py:846; EDMarketConnector.py:849; */
"Edit" = "Appunti";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:832; */
"View" = "Vista";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:833; */
"Window" = "Finestra";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:834; EDMarketConnector.py:847; EDMarketConnector.py:850; */
"Help" = "Guida";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:837; EDMarketConnector.py:863; EDMarketConnector.py:1566; */
"About {APP}" = "Informazioni su {APP}";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:839; EDMarketConnector.py:862; */
"Check for Updates..." = "Controlla aggiornamenti...";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:840; EDMarketConnector.py:854; */
"Save Raw Data..." = "Salva i Dati Grezzi...";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:841; EDMarketConnector.py:853; stats.py:406; */
"Status" = "Status";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:842; EDMarketConnector.py:860; */
"Privacy Policy" = "Informativa sulla privacy";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:843; EDMarketConnector.py:861; EDMarketConnector.py:1600; */
"Release Notes" = "Note di rilascio";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:855; EDMarketConnector.py:2020; prefs.py:255; */
"Settings" = "Impostazioni";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:856; */
"Exit" = "Esci";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:859; */
"Documentation" = "Documentazione";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:872; */
"Logging in..." = "Mi sto loggando...";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:888; EDMarketConnector.py:1431; */
"Authentication successful" = "Autenticazione riuscita";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:919; */
"You're not docked at a station!" = "Non sei parcheggiato in nessuna stazione !";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:927; */
"Station doesn't have anything!" = "La Stazione non ha niente !";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:932; */
"Station doesn't have a market!" = "La Stazione non ha un Market !";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1006; */
"Fetching data..." = "Sto raccogliendo i dati...";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1051; stats.py:335; */
"Who are you?!" = "Chi sei ?!";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1057; stats.py:345; */
"Where are you?!" = "Dove sei finito ?!";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1064; stats.py:353; */
"What are you flying?!" = "Stai volando ?!";
/* EDMarketConnector.py: Frontier CAPI server error when fetching data; In files: EDMarketConnector.py:1176; */
"Frontier CAPI server error" = "Errore nel server Frontier CAPI";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1226; */
"Last updated at %H:%M:%S" = "Ultimo aggiornamento alle %H:%M:%S";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1254; */
"Fighter" = "Cacciatore";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1255; */
"Gunner" = "Artigliere";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1256; */
"Helm" = "Timoniere";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1514; */
"cooldown {SS}s" = "disponibile tra {SS} secondi";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1626; prefs.py:306; */
"OK" = "OK";
/* EDMarketConnector.py: The application is shutting down; In files: EDMarketConnector.py:1693; */
"Shutting down..." = "Spegnimento...";
/* EDMarketConnector.py: Popup-text about 'active' plugins without Python 3.x support; In files: EDMarketConnector.py:2008:2014; */
"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." = "Uno o più dei tuoi plugin non ha ancora il supporto per Python 3.x. Per favore guarda la lista '{PLUGINS}' nella tab a '{FILE}' > '{SETTINGS}'. Dovresti controllare se ci sono aggiornamenti, altrimenti avvisa gli sviluppatori che devono aggiornare il codice a Python 3.x.\n\nPuoi disabilitare il plugin rinominando la cartella aggiungendo '{DISABLED}' alla fine del nome.";
/* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2018; prefs.py:978; */
"Plugins" = "Plugins";
/* EDMarketConnector.py: Popup window title for list of 'enabled' plugins that don't work with Python 3.x; In files: EDMarketConnector.py:2029; */
"EDMC: Plugins Without Python 3.x Support" = "EDMC: Plugins senza Supporto Python 3.x";
/* companion.py: Frontier CAPI data doesn't agree with latest Journal game location; In files: companion.py:237; */
/* companion.py: Frontier CAPI data doesn't agree with latest Journal game location; In files: companion.py:245; */
"Error: Frontier server is lagging" = "Errore: il server Frontier sta laggando";
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:263; */
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:271; */
"Error: Invalid Credentials" = "Errore: Credenziali non valide";
/* companion.py: Frontier CAPI authorisation not for currently game-active commander; In files: companion.py:288; */
/* companion.py: Frontier CAPI authorisation not for currently game-active commander; In files: companion.py:296; */
"Error: Wrong Cmdr" = "Errore: Cmdr sbagliato";
/* companion.py: Generic error prefix - following text is from Frontier auth service; In files: companion.py:414; companion.py:499; */
/* companion.py: Generic error prefix - following text is from Frontier auth service; In files: companion.py:432; companion.py:517; */
"Error" = "Errore";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:194; prefs.py:468; prefs.py:702; prefs.py:735; */
/* EDMarketConnector.py: Main UI Update button; EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:601; EDMarketConnector.py:919; EDMarketConnector.py:1748; */
"Update" = "Recupera";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:710; prefs.py:875; */
"Always on top" = "Sempre in primo piano";
/* EDMarketConnector.py: Unknown suit; In files: EDMarketConnector.py:837; */
"Unknown" = "Sconosciuto";
/* EDMarketConnector.py: Label for commander name in main window; edsm.py: Game Commander name label in EDSM settings; stats.py: Cmdr stats; theme.py: Label for commander name in main window; In files: EDMarketConnector.py:913; edsm.py:332; stats.py:57; theme.py:290; */
"Cmdr" = "Cmdr";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:915; EDMarketConnector.py:1487; */
"Role" = "Ruolo";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:915; EDMarketConnector.py:1497; EDMarketConnector.py:1520; stats.py:405; */
"Ship" = "Nave";
/* EDMarketConnector.py: Label for 'Suit' line in main UI; In files: EDMarketConnector.py:916; */
"Suit" = "Tuta";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:917; prefs.py:614; stats.py:407; */
"System" = "Sistema";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:918; prefs.py:632; prefs.py:770; stats.py:408; */
"Station" = "Stazione";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:921; EDMarketConnector.py:939; EDMarketConnector.py:942; EDMarketConnector.py:2264; */
"File" = "File";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:922; EDMarketConnector.py:940; EDMarketConnector.py:943; */
"Edit" = "Appunti";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:923; */
"View" = "Vista";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:924; */
"Window" = "Finestra";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:925; EDMarketConnector.py:941; EDMarketConnector.py:944; */
"Help" = "Guida";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:928; EDMarketConnector.py:959; EDMarketConnector.py:1804; */
"About {APP}" = "Informazioni su {APP}";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:930; EDMarketConnector.py:958; */
"Check for Updates..." = "Controlla aggiornamenti...";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:931; EDMarketConnector.py:948; */
"Save Raw Data..." = "Salva i Dati Grezzi...";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:932; EDMarketConnector.py:947; stats.py:402; */
"Status" = "Status";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:933; EDMarketConnector.py:953; */
"Documentation" = "Documentazione";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:936; EDMarketConnector.py:956; */
"Privacy Policy" = "Informativa sulla privacy";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:937; EDMarketConnector.py:957; EDMarketConnector.py:1838; */
"Release Notes" = "Note di rilascio";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:949; EDMarketConnector.py:2265; prefs.py:241; */
"Settings" = "Impostazioni";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:950; */
"Exit" = "Esci";
/* EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; In files: EDMarketConnector.py:962; ttkHyperlinkLabel.py:53; */
"Copy" = "Copia";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:978; */
"Logging in..." = "Mi sto loggando...";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:994; EDMarketConnector.py:1657; */
"Authentication successful" = "Autenticazione riuscita";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:1025; */
"You're not docked at a station!" = "Non sei parcheggiato in nessuna stazione !";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:1033; */
"Station doesn't have anything!" = "La Stazione non ha niente !";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:1038; */
"Station doesn't have a market!" = "La Stazione non ha un Market !";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1128; EDMarketConnector.py:1179; */
"Fetching data..." = "Sto raccogliendo i dati...";
/* EDMarketConnector.py: No data was returned for the commander from the Frontier CAPI; In files: EDMarketConnector.py:1242; */
"CAPI: No commander data returned" = "CAPI: Nessun dato sul commandante";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1246; stats.py:333; */
"Who are you?!" = "Chi sei ?!";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1252; stats.py:341; */
"Where are you?!" = "Dove sei finito ?!";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1259; stats.py:349; */
"What are you flying?!" = "Stai volando ?!";
/* EDMarketConnector.py: Frontier CAPI server error when fetching data; In files: EDMarketConnector.py:1384; */
"Frontier CAPI server error" = "Errore nel server Frontier CAPI";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1434; */
"Last updated at %H:%M:%S" = "Ultimo aggiornamento alle %H:%M:%S";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1462; */
"Fighter" = "Cacciatore";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1463; */
"Gunner" = "Artigliere";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1464; */
"Helm" = "Timoniere";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1742; */
"cooldown {SS}s" = "disponibile tra {SS} secondi";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1864; prefs.py:292; */
"OK" = "OK";
/* EDMarketConnector.py: The application is shutting down; In files: EDMarketConnector.py:1936; */
"Shutting down..." = "Spegnimento...";
/* EDMarketConnector.py: Popup-text about 'active' plugins without Python 3.x support; In files: EDMarketConnector.py:2253:2259; */
"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." = "Uno o più dei tuoi plugin non ha ancora il supporto per Python 3.x. Per favore guarda la lista '{PLUGINS}' nella tab a '{FILE}' > '{SETTINGS}'. Dovresti controllare se ci sono aggiornamenti, altrimenti avvisa gli sviluppatori che devono aggiornare il codice a Python 3.x.\n\nPuoi disabilitare il plugin rinominando la cartella aggiungendo '{DISABLED}' alla fine del nome.";
/* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2263; prefs.py:986; */
"Plugins" = "Plugins";
/* EDMarketConnector.py: Popup window title for list of 'enabled' plugins that don't work with Python 3.x; In files: EDMarketConnector.py:2274; */
"EDMC: Plugins Without Python 3.x Support" = "EDMC: Plugins senza Supporto Python 3.x";
/* journal_lock.py: Title text on popup when Journal directory already locked; In files: journal_lock.py:208; */
"Journal directory already locked" = "cartella Journal già bloccata";
/* journal_lock.py: Text for when newly selected Journal directory is already locked; In files: journal_lock.py:225:226; */
"The new Journal Directory location is already locked.{CR}You can either attempt to resolve this and then Retry, or choose to Ignore this." = "La nuova cartella dove risiede il Journal è già bloccata.{CR}Puoi provare a risolvere Riprovando, oppure scegliere di ignorare la cosa.";
/* journal_lock.py: Generic 'Retry' button label; In files: journal_lock.py:230; */
"Retry" = "Riprova";
/* journal_lock.py: Generic 'Ignore' button label; In files: journal_lock.py:234; */
"Ignore" = "Ignora";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:193; prefs.py:455; prefs.py:709; prefs.py:742; */
"Default" = "Predefinito";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:251; */
/* coriolis.py: 'Auto' label for Coriolis site override selection; coriolis.py: Coriolis normal/beta selection - auto; In files: coriolis.py:74; coriolis.py:77; coriolis.py:123; coriolis.py:139; coriolis.py:145; */
"Auto" = "Auto";
/* coriolis.py: 'Normal' label for Coriolis site override selection; coriolis.py: Coriolis normal/beta selection - normal; In files: coriolis.py:75; coriolis.py:121; coriolis.py:137; */
"Normal" = "Normale";
/* coriolis.py: 'Beta' label for Coriolis site override selection; coriolis.py: Coriolis normal/beta selection - beta; In files: coriolis.py:76; coriolis.py:122; coriolis.py:138; */
"Beta" = "Beta";
/* coriolis.py: Settings>Coriolis: Label for 'NOT alpha/beta game version' URL; In files: coriolis.py:97; */
"Normal URL" = "URL normale";
/* coriolis.py: Generic 'Reset' button label; In files: coriolis.py:100; coriolis.py:109; */
"Reset" = "Reimposta";
/* coriolis.py: Settings>Coriolis: Label for 'alpha/beta game version' URL; In files: coriolis.py:106; */
"Beta URL" = "URL Beta";
/* coriolis.py: Settings>Coriolis: Label for selection of using Normal, Beta or 'auto' Coriolis URL; In files: coriolis.py:116; */
"Override Beta/Normal Selection" = "Sovrascrivi selezione Beta/Normale";
/* eddb.py: Journal Processing disabled due to an active killswitch; In files: eddb.py:179; */
"EDDB Journal processing disabled. See Log." = "Elaborazione di EDDB Journal disabilitata. Guarda il Log.";
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:458; eddn.py:2413; eddn.py:2451; eddn.py:2519; */
"Error: Can't connect to EDDN" = "Errore: Non riesco a connettermi a EDDN";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:2041; */
"Send station data to the Elite Dangerous Data Network" = "Manda i dati della stazione a Elite Dangerous Data Network";
/* eddn.py: Enable EDDN support for system and other scan data checkbox label; In files: eddn.py:2052; */
"Send system and scan data to the Elite Dangerous Data Network" = "Manda il sistema e i dati scansionati a EDDN";
/* eddn.py: EDDN delay sending until docked option is on, this message notes that a send was skipped due to this; In files: eddn.py:2063; */
"Delay sending until docked" = "Ritarda l'invio fino a che si è ormeggiati";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:2507; */
"Sending data to EDDN..." = "Sto inviando i dati a EDDN...";
/* edsm.py: Settings>EDSM - Label on checkbox for 'send data'; In files: edsm.py:308; */
"Send flight log and Cmdr status to EDSM" = "Manda il flight log e lo stato Cmdr a EDSM";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:319; */
"Elite Dangerous Star Map credentials" = "Credenziali per Elite Dangerous Star Map";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:341; */
"Commander Name" = "Nome Commandante";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:350; inara.py:278; */
"API Key" = "API Key";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:394; prefs.py:527; prefs.py:1157; prefs.py:1190; stats.py:154; stats.py:173; stats.py:192; stats.py:209; */
"None" = "Nessuna";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:916; edsm.py:1048; */
"Error: EDSM {MSG}" = "Errore: EDSM {MSG}";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:953; edsm.py:1043; */
"Error: Can't connect to EDSM" = "Errore: Non riesco a connettermi a EDSM";
/* inara.py: Checkbox to enable INARA API Usage; In files: inara.py:257; */
"Send flight log and Cmdr status to Inara" = "Manda il flight log e lo stato Cmdr a Inara";
/* inara.py: Text for INARA API keys link ( goes to https://inara.cz/settings-api ); In files: inara.py:269; */
"Inara credentials" = "Credenziali per Inara";
/* inara.py: INARA support disabled via killswitch; In files: inara.py:395; */
"Inara disabled. See Log." = "Inara disabilitato. Vedi il Log.";
/* inara.py: INARA API returned some kind of error (error message will be contained in {MSG}); In files: inara.py:1650; inara.py:1663; */
"Error: Inara {MSG}" = "Errore: Inara {MSG}";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:237; */
"Preferences" = "Preferenze";
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:348; */
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:335; */
"Please choose what data to save" = "Si prega di scegliere che dati salvare";
/* prefs.py: Settings > Output option; In files: prefs.py:354; */
/* prefs.py: Settings > Output option; In files: prefs.py:341; */
"Market data in CSV format file" = "Market data in formato CSV";
/* prefs.py: Settings > Output option; In files: prefs.py:363; */
/* prefs.py: Settings > Output option; In files: prefs.py:350; */
"Market data in Trade Dangerous format file" = "Market data in formato Trade Dangerous";
/* prefs.py: Settings > Output option; In files: prefs.py:373; */
/* prefs.py: Settings > Output option; In files: prefs.py:360; */
"Ship loadout" = "Equipaggiamento della nave";
/* prefs.py: Settings > Output option; In files: prefs.py:383; */
/* prefs.py: Settings > Output option; In files: prefs.py:370; */
"Automatically update on docking" = "Auto-aggiornamento all'ormeggio";
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:392; prefs.py:411; */
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:379; prefs.py:398; */
"File location" = "Percorso del file";
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:400; prefs.py:450; */
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:387; prefs.py:437; */
"Change..." = "Cambia...";
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:403; prefs.py:453; */
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:390; prefs.py:440; */
"Browse..." = "Sfoglia...";
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:418; */
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:405; */
"Output" = "Output";
/* prefs.py: Settings > Configuration - Label for Journal files location; In files: prefs.py:444; prefs.py:459; */
/* prefs.py: Settings > Configuration - Label for Journal files location; In files: prefs.py:431; prefs.py:446; */
"E:D journal file location" = "locazione del file E:D journal";
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:484; */
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:490; */
"Keyboard shortcut" = "Scorciatoia di tastiera";
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:486; */
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:492; */
"Hotkey" = "Hotkey";
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:495; */
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:501; */
"Re-start {APP} to use shortcuts" = "Riavvia {APP} per usare le scorciatoie";
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:504; */
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:510; */
"{APP} needs permission to use shortcuts" = "{APP} ha bisogno dei permessi per usare le scorciatoie";
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:509; */
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:515; */
"Open System Preferences" = "Apri Preferenze di Sistema";
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:531; */
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:538; */
"Only when Elite: Dangerous is the active app" = "Solo quando “Elite: Dangerous” è in primo piano";
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:542; */
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:549; */
"Play sound" = "Notifica sonora";
/* prefs.py: Configuration - disable checks for app updates when in-game; In files: prefs.py:557; */
/* prefs.py: Configuration - disable checks for app updates when in-game; In files: prefs.py:564; */
"Disable Automatic Application Updates Check when in-game" = "Disabilita gli aggiornamenti automatici quando sei in-game";
/* prefs.py: Label for preferred shipyard, system and station 'providers'; In files: prefs.py:570; */
/* prefs.py: Label for preferred shipyard, system and station 'providers'; In files: prefs.py:577; */
"Preferred websites" = "Sito web preferito";
/* prefs.py: Label for Shipyard provider selection; In files: prefs.py:581; */
/* prefs.py: Label for Shipyard provider selection; In files: prefs.py:588; */
"Shipyard" = "Spazioporto";
/* prefs.py: Label for checkbox to utilise alternative Coriolis URL method; In files: prefs.py:593; */
/* prefs.py: Label for checkbox to utilise alternative Coriolis URL method; In files: prefs.py:600; */
"Use alternate URL method" = "Usare metodo URL alternativo";
/* prefs.py: Configuration - Label for selection of Log Level; In files: prefs.py:646; */
/* prefs.py: Configuration - Label for selection of Log Level; In files: prefs.py:653; */
"Log Level" = "Livello di Log";
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:674; */
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:681; */
"Configuration" = "Configurazione";
/* prefs.py: Preferences privacy tab title; In files: prefs.py:696; */
/* prefs.py: Preferences privacy tab title; In files: prefs.py:703; */
"Privacy" = "Dati personali";
/* prefs.py: Label for Settings > Appeareance > selection of 'normal' text colour; In files: prefs.py:709; */
/* prefs.py: Label for Settings > Appeareance > selection of 'normal' text colour; In files: prefs.py:716; */
"Normal text" = "Testo normale";
/* prefs.py: Label for Settings > Appeareance > selection of 'highlightes' text colour; In files: prefs.py:711; */
/* prefs.py: Label for Settings > Appeareance > selection of 'highlightes' text colour; In files: prefs.py:718; */
"Highlighted text" = "Testo evidenziato";
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:720; */
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:727; */
"Language" = "Lingua";
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:730; */
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:737; */
"Theme" = "Tema";
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:741; */
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:749; */
"Dark" = "Scuro";
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:748; */
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:756; */
"Transparent" = "Trasparente";
/* prefs.py: Appearance - Label for selection of UI scaling; In files: prefs.py:794; */
/* prefs.py: Appearance - Label for selection of UI scaling; In files: prefs.py:802; */
"UI Scale Percentage" = "Percentuale zoom della UI";
/* prefs.py: Appearance - Help/hint text for UI scaling selection; In files: prefs.py:815; */
/* prefs.py: Appearance - Help/hint text for UI scaling selection; In files: prefs.py:823; */
"100 means Default{CR}Restart Required for{CR}changes to take effect!" = "100 significa Default{CR}Riavvio Richiesto per{CR}applicare i cambiamenti!";
/* prefs.py: Appearance - Label for selection of main window transparency; In files: prefs.py:825; */
/* prefs.py: Appearance - Label for selection of main window transparency; In files: prefs.py:833; */
"Main window transparency" = "Trasparenza della finestra principale";
/* prefs.py: Appearance - Help/hint text for Main window transparency selection; In files: prefs.py:845:848; */
/* prefs.py: Appearance - Help/hint text for Main window transparency selection; In files: prefs.py:853:856; */
"100 means fully opaque.{CR}Window is updated in real time" = "100 stà per completamente opaco.{CR}Finestra è aggiornata in tempo reale";
/* prefs.py: Appearance option for Windows "minimize to system tray"; In files: prefs.py:877; */
/* prefs.py: Appearance option for Windows "minimize to system tray"; In files: prefs.py:885; */
"Minimize to system tray" = "Riduci a icona nel Vassoio di sistema";
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:885; */
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:893; */
"Appearance" = "Aspetto";
/* prefs.py: Label for location of third-party plugins folder; In files: prefs.py:900; */
/* prefs.py: Label for location of third-party plugins folder; In files: prefs.py:908; */
"Plugins folder" = "Cartella dei plugins";
/* prefs.py: Label on button used to open a filesystem folder; In files: prefs.py:907; */
/* prefs.py: Label on button used to open a filesystem folder; In files: prefs.py:915; */
"Open" = "Apri";
/* prefs.py: Tip/label about how to disable plugins; In files: prefs.py:915; */
/* prefs.py: Tip/label about how to disable plugins; In files: prefs.py:923; */
"Tip: You can disable a plugin by{CR}adding '{EXT}' to its folder name" = "Suggerimento: è possibile disabilitare un plugin {CR}aggiungendo '{EXT}' nel nome della sua cartella";
/* prefs.py: Label on list of enabled plugins; In files: prefs.py:926; */
/* prefs.py: Label on list of enabled plugins; In files: prefs.py:934; */
"Enabled Plugins" = "Plugin abilitati";
/* prefs.py: Plugins - Label for list of 'enabled' plugins that don't work with Python 3.x; In files: prefs.py:946; */
/* prefs.py: Plugins - Label for list of 'enabled' plugins that don't work with Python 3.x; In files: prefs.py:954; */
"Plugins Without Python 3.x Support:" = "Plugin senza il supporto a Python 3.x :";
/* prefs.py: Plugins - Label on URL to documentation about migrating plugins from Python 2.7; In files: prefs.py:954; */
/* prefs.py: Plugins - Label on URL to documentation about migrating plugins from Python 2.7; In files: prefs.py:962; */
"Information on migrating plugins" = "Informazioni sulla migrazione dei plugins";
/* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:969; */
/* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:977; */
"Disabled Plugins" = "Plugin disabilitati";
/* stats.py: Cmdr stats; In files: stats.py:60; */
/* stats.py: Cmdr stats; In files: stats.py:58; */
"Balance" = "Saldo";
/* stats.py: Cmdr stats; In files: stats.py:61; */
/* stats.py: Cmdr stats; In files: stats.py:59; */
"Loan" = "Prestito";
/* stats.py: Top rank; In files: stats.py:65; */
/* stats.py: Top rank; In files: stats.py:63; */
"Elite" = "Elite";
/* stats.py: Ranking; In files: stats.py:76; */
/* stats.py: Ranking; In files: stats.py:74; */
"Combat" = "Combat";
/* stats.py: Ranking; In files: stats.py:77; */
/* stats.py: Ranking; In files: stats.py:75; */
"Trade" = "Trade";
/* stats.py: Ranking; In files: stats.py:78; */
/* stats.py: Ranking; In files: stats.py:76; */
"Explorer" = "Explorer";
/* stats.py: Ranking; In files: stats.py:81; */
/* stats.py: Ranking; In files: stats.py:79; */
"CQC" = "CQC";
/* stats.py: Ranking; In files: stats.py:82; */
/* stats.py: Ranking; In files: stats.py:80; */
"Federation" = "Federazione";
/* stats.py: Ranking; In files: stats.py:83; */
/* stats.py: Ranking; In files: stats.py:81; */
"Empire" = "Impero";
/* stats.py: Ranking; In files: stats.py:84; */
/* stats.py: Ranking; In files: stats.py:82; */
"Powerplay" = "Powerplay";
/* stats.py: Combat rank; In files: stats.py:93; */
/* stats.py: Combat rank; In files: stats.py:91; */
"Harmless" = "Harmless";
/* stats.py: Combat rank; In files: stats.py:94; */
/* stats.py: Combat rank; In files: stats.py:92; */
"Mostly Harmless" = "Mostly Harmless";
/* stats.py: Combat rank; In files: stats.py:95; */
/* stats.py: Combat rank; In files: stats.py:93; */
"Novice" = "Novice";
/* stats.py: Combat rank; In files: stats.py:96; */
/* stats.py: Combat rank; In files: stats.py:94; */
"Competent" = "Competent";
/* stats.py: Combat rank; In files: stats.py:97; */
/* stats.py: Combat rank; In files: stats.py:95; */
"Expert" = "Expert";
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:98; stats.py:178; */
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:96; stats.py:176; */
"Master" = "Master";
/* stats.py: Combat rank; In files: stats.py:99; */
/* stats.py: Combat rank; In files: stats.py:97; */
"Dangerous" = "Dangerous";
/* stats.py: Combat rank; In files: stats.py:100; */
/* stats.py: Combat rank; In files: stats.py:98; */
"Deadly" = "Deadly";
/* stats.py: Trade rank; In files: stats.py:103; */
/* stats.py: Trade rank; In files: stats.py:101; */
"Penniless" = "Penniless";
/* stats.py: Trade rank; In files: stats.py:104; */
/* stats.py: Trade rank; In files: stats.py:102; */
"Mostly Penniless" = "Mostly Penniless";
/* stats.py: Trade rank; In files: stats.py:105; */
/* stats.py: Trade rank; In files: stats.py:103; */
"Peddler" = "Peddler";
/* stats.py: Trade rank; In files: stats.py:106; */
/* stats.py: Trade rank; In files: stats.py:104; */
"Dealer" = "Dealer";
/* stats.py: Trade rank; In files: stats.py:107; */
/* stats.py: Trade rank; In files: stats.py:105; */
"Merchant" = "Merchant";
/* stats.py: Trade rank; In files: stats.py:108; */
/* stats.py: Trade rank; In files: stats.py:106; */
"Broker" = "Broker";
/* stats.py: Trade rank; In files: stats.py:109; */
/* stats.py: Trade rank; In files: stats.py:107; */
"Entrepreneur" = "Entrepreneur";
/* stats.py: Trade rank; In files: stats.py:110; */
/* stats.py: Trade rank; In files: stats.py:108; */
"Tycoon" = "Tycoon";
/* stats.py: Explorer rank; In files: stats.py:113; */
/* stats.py: Explorer rank; In files: stats.py:111; */
"Aimless" = "Aimless";
/* stats.py: Explorer rank; In files: stats.py:114; */
/* stats.py: Explorer rank; In files: stats.py:112; */
"Mostly Aimless" = "Mostly Aimless";
/* stats.py: Explorer rank; In files: stats.py:115; */
/* stats.py: Explorer rank; In files: stats.py:113; */
"Scout" = "Scout";
/* stats.py: Explorer rank; In files: stats.py:116; */
/* stats.py: Explorer rank; In files: stats.py:114; */
"Surveyor" = "Surveyor";
/* stats.py: Explorer rank; In files: stats.py:117; */
/* stats.py: Explorer rank; In files: stats.py:115; */
"Trailblazer" = "Trailblazer";
/* stats.py: Explorer rank; In files: stats.py:118; */
/* stats.py: Explorer rank; In files: stats.py:116; */
"Pathfinder" = "Pathfinder";
/* stats.py: Explorer rank; In files: stats.py:119; */
/* stats.py: Explorer rank; In files: stats.py:117; */
"Ranger" = "Ranger";
/* stats.py: Explorer rank; In files: stats.py:120; */
/* stats.py: Explorer rank; In files: stats.py:118; */
"Pioneer" = "Pioneer";
/* stats.py: CQC rank; In files: stats.py:144; */
/* stats.py: CQC rank; In files: stats.py:142; */
"Helpless" = "Helpless";
/* stats.py: CQC rank; In files: stats.py:145; */
/* stats.py: CQC rank; In files: stats.py:143; */
"Mostly Helpless" = "Mostly Helpless";
/* stats.py: CQC rank; In files: stats.py:146; */
/* stats.py: CQC rank; In files: stats.py:144; */
"Amateur" = "Amateur";
/* stats.py: CQC rank; In files: stats.py:147; */
/* stats.py: CQC rank; In files: stats.py:145; */
"Semi Professional" = "Semi Professional";
/* stats.py: CQC rank; In files: stats.py:148; */
/* stats.py: CQC rank; In files: stats.py:146; */
"Professional" = "Professional";
/* stats.py: CQC rank; In files: stats.py:149; */
/* stats.py: CQC rank; In files: stats.py:147; */
"Champion" = "Champion";
/* stats.py: CQC rank; In files: stats.py:150; */
/* stats.py: CQC rank; In files: stats.py:148; */
"Hero" = "Hero";
/* stats.py: CQC rank; In files: stats.py:151; */
/* stats.py: CQC rank; In files: stats.py:149; */
"Gladiator" = "Gladiator";
/* stats.py: Federation rank; In files: stats.py:157; */
/* stats.py: Federation rank; In files: stats.py:155; */
"Recruit" = "Recruit";
/* stats.py: Federation rank; In files: stats.py:158; */
/* stats.py: Federation rank; In files: stats.py:156; */
"Cadet" = "Cadet";
/* stats.py: Federation rank; In files: stats.py:159; */
/* stats.py: Federation rank; In files: stats.py:157; */
"Midshipman" = "Midshipman";
/* stats.py: Federation rank; In files: stats.py:160; */
/* stats.py: Federation rank; In files: stats.py:158; */
"Petty Officer" = "Petty Officer";
/* stats.py: Federation rank; In files: stats.py:161; */
/* stats.py: Federation rank; In files: stats.py:159; */
"Chief Petty Officer" = "Chief Petty Officer";
/* stats.py: Federation rank; In files: stats.py:162; */
/* stats.py: Federation rank; In files: stats.py:160; */
"Warrant Officer" = "Warrant Officer";
/* stats.py: Federation rank; In files: stats.py:163; */
/* stats.py: Federation rank; In files: stats.py:161; */
"Ensign" = "Ensign";
/* stats.py: Federation rank; In files: stats.py:164; */
/* stats.py: Federation rank; In files: stats.py:162; */
"Lieutenant" = "Lieutenant";
/* stats.py: Federation rank; In files: stats.py:165; */
/* stats.py: Federation rank; In files: stats.py:163; */
"Lieutenant Commander" = "Lieutenant Commander";
/* stats.py: Federation rank; In files: stats.py:166; */
/* stats.py: Federation rank; In files: stats.py:164; */
"Post Commander" = "Post Commander";
/* stats.py: Federation rank; In files: stats.py:167; */
/* stats.py: Federation rank; In files: stats.py:165; */
"Post Captain" = "Post Captain";
/* stats.py: Federation rank; In files: stats.py:168; */
/* stats.py: Federation rank; In files: stats.py:166; */
"Rear Admiral" = "Rear Admiral";
/* stats.py: Federation rank; In files: stats.py:169; */
/* stats.py: Federation rank; In files: stats.py:167; */
"Vice Admiral" = "Vice Admiral";
/* stats.py: Federation rank; In files: stats.py:170; */
/* stats.py: Federation rank; In files: stats.py:168; */
"Admiral" = "Admiral";
/* stats.py: Empire rank; In files: stats.py:176; */
/* stats.py: Empire rank; In files: stats.py:174; */
"Outsider" = "Outsider";
/* stats.py: Empire rank; In files: stats.py:177; */
/* stats.py: Empire rank; In files: stats.py:175; */
"Serf" = "Serf";
/* stats.py: Empire rank; In files: stats.py:179; */
/* stats.py: Empire rank; In files: stats.py:177; */
"Squire" = "Squire";
/* stats.py: Empire rank; In files: stats.py:180; */
/* stats.py: Empire rank; In files: stats.py:178; */
"Knight" = "Knight";
/* stats.py: Empire rank; In files: stats.py:181; */
/* stats.py: Empire rank; In files: stats.py:179; */
"Lord" = "Lord";
/* stats.py: Empire rank; In files: stats.py:182; */
/* stats.py: Empire rank; In files: stats.py:180; */
"Baron" = "Baron";
/* stats.py: Empire rank; In files: stats.py:183; */
/* stats.py: Empire rank; In files: stats.py:181; */
"Viscount" = "Viscount";
/* stats.py: Empire rank; In files: stats.py:184; */
/* stats.py: Empire rank; In files: stats.py:182; */
"Count" = "Count";
/* stats.py: Empire rank; In files: stats.py:185; */
/* stats.py: Empire rank; In files: stats.py:183; */
"Earl" = "Earl";
/* stats.py: Empire rank; In files: stats.py:186; */
/* stats.py: Empire rank; In files: stats.py:184; */
"Marquis" = "Marquis";
/* stats.py: Empire rank; In files: stats.py:187; */
/* stats.py: Empire rank; In files: stats.py:185; */
"Duke" = "Duke";
/* stats.py: Empire rank; In files: stats.py:188; */
/* stats.py: Empire rank; In files: stats.py:186; */
"Prince" = "Prince";
/* stats.py: Empire rank; In files: stats.py:189; */
/* stats.py: Empire rank; In files: stats.py:187; */
"King" = "King";
/* stats.py: Power rank; In files: stats.py:195; */
/* stats.py: Power rank; In files: stats.py:193; */
"Rating 1" = "Rating 1";
/* stats.py: Power rank; In files: stats.py:196; */
/* stats.py: Power rank; In files: stats.py:194; */
"Rating 2" = "Rating 2";
/* stats.py: Power rank; In files: stats.py:197; */
/* stats.py: Power rank; In files: stats.py:195; */
"Rating 3" = "Rating 3";
/* stats.py: Power rank; In files: stats.py:198; */
/* stats.py: Power rank; In files: stats.py:196; */
"Rating 4" = "Rating 4";
/* stats.py: Power rank; In files: stats.py:199; */
/* stats.py: Power rank; In files: stats.py:197; */
"Rating 5" = "Rating 5";
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:413; */
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:409; */
"Value" = "Valore";
/* stats.py: Status dialog title; In files: stats.py:422; */
/* stats.py: Status dialog title; In files: stats.py:418; */
"Ships" = "Navi";
+453 -429
View File
File diff suppressed because it is too large. Load diff
+395 -395
View File
File diff suppressed because it is too large. Load diff
+258 -258
View File
@@ -1,482 +1,482 @@
/* Language name */
"!Language" = "Latviešu";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:212; eddn.py:584; eddn.py:925; load.py:215; load.py:593; load.py:940; eddn.py:252; eddn.py:699; eddn.py:1439; */
"Sending data to EDDN..." = "Sūta datus uz EDDN...";
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:257; eddn.py:864; eddn.py:898; eddn.py:937; load.py:260; load.py:878; load.py:913; load.py:952; eddn.py:316; eddn.py:1375; eddn.py:1410; eddn.py:1451; */
"Error: Can't connect to EDDN" = "Kļūda: Nevar savienoties ar EDDN";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:663; load.py:672; eddn.py:1122; */
"Send station data to the Elite Dangerous Data Network" = "Sūtīt stacijas datus uz Elite Dangerous Data Network";
/* eddn.py: Enable EDDN support for system and other scan data checkbox label; In files: eddn.py:673; load.py:682; eddn.py:1133; */
"Send system and scan data to the Elite Dangerous Data Network" = "Sūtīt stacijas datus uz Elite Dangerous Data Network";
/* eddn.py: EDDN delay sending until docked option is on, this message notes that a send was skipped due to this; In files: eddn.py:683; load.py:692; eddn.py:1144; */
"Delay sending until docked" = "Aizkavēt sūtīšanu līdz nosēšanai";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:206; load.py:206; edsm.py:247; */
"Elite Dangerous Star Map credentials" = "Elite Dangerous zvaigžņu kartes informācija";
/* edsm.py: Game Commander name label in EDSM settings; theme.py: Label for commander name in main window; EDMarketConnector.py: Label for commander name in main window; stats.py: Cmdr stats; In files: edsm.py:216; load.py:216; edsm.py:258; theme.py:227; EDMarketConnector.py:822; stats.py:59; */
"Cmdr" = "Cmdr";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:223; load.py:223; edsm.py:266; */
"Commander Name" = "Komandiera vārds";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:230; inara.py:233; load.py:230; load.py:233; edsm.py:274; inara.py:243; */
"API Key" = "API atslēga";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:256; load.py:256; edsm.py:301; prefs.py:520; prefs.py:1174; prefs.py:1207; stats.py:156; stats.py:175; stats.py:194; stats.py:211; */
"None" = "Neviens";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:622; edsm.py:724; load.py:627; load.py:729; edsm.py:747; edsm.py:875; */
"Error: EDSM {MSG}" = "Kļūda: EDSM {MSG}";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:658; edsm.py:720; load.py:663; load.py:725; edsm.py:784; edsm.py:870; */
"Error: Can't connect to EDSM" = "Kļūda: Nevar savienoties ar EDSM";
/* ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; In files: ttkHyperlinkLabel.py:42; EDMarketConnector.py:866; */
"Copy" = "Kopēt";
/* EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:529; EDMarketConnector.py:828; EDMarketConnector.py:1520; */
"Update" = "Atjaunināt";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:612; prefs.py:867; */
"Always on top" = "Vienmēr virspusē";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:824; EDMarketConnector.py:1279; */
"Role" = "Loma";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:824; EDMarketConnector.py:1289; EDMarketConnector.py:1312; stats.py:409; */
"Ship" = "Kuģis";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:826; prefs.py:607; stats.py:411; */
"System" = "Sistēma";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:827; prefs.py:625; prefs.py:762; stats.py:412; */
"Station" = "Stacija";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:830; EDMarketConnector.py:845; EDMarketConnector.py:848; EDMarketConnector.py:2019; */
"File" = "Fails";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:831; EDMarketConnector.py:846; EDMarketConnector.py:849; */
"Edit" = "Labot";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:832; */
"View" = "Skats";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:833; */
"Window" = "Logs";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:834; EDMarketConnector.py:847; EDMarketConnector.py:850; */
"Help" = "Palīdzība";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:837; EDMarketConnector.py:863; EDMarketConnector.py:1566; */
"About {APP}" = "Par {APP}";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:839; EDMarketConnector.py:862; */
"Check for Updates..." = "Pārbaudīt atjauninājumus...";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:840; EDMarketConnector.py:854; */
"Save Raw Data..." = "Saglabāt sākotnējos datus...";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:841; EDMarketConnector.py:853; stats.py:406; */
"Status" = "Statuss";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:842; EDMarketConnector.py:860; */
"Privacy Policy" = "Privātuma politika";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:843; EDMarketConnector.py:861; EDMarketConnector.py:1600; */
"Release Notes" = "Informācija par laidienu";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:855; EDMarketConnector.py:2020; prefs.py:255; */
"Settings" = "Iestatījumi";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:856; */
"Exit" = "Iziet";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:859; */
"Documentation" = "Dokumentācija";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:872; */
"Logging in..." = "Autorizējas...";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:888; EDMarketConnector.py:1431; */
"Authentication successful" = "Autentifikācija sekmīga";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:919; */
"You're not docked at a station!" = "Jūs neesat stacijā!";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:927; */
"Station doesn't have anything!" = "Stacijā nekā nav!";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:932; */
"Station doesn't have a market!" = "Stacijā nav tirgus!";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1006; */
"Fetching data..." = "Iegūst datus...";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1051; stats.py:335; */
"Who are you?!" = "Kas tu esi?";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1057; stats.py:345; */
"Where are you?!" = "Kur tu esi?";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1064; stats.py:353; */
"What are you flying?!" = "Ar ko tu lido?";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1226; */
"Last updated at %H:%M:%S" = "Pēdējo reizi atjaunināts %H:%M:%S";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1254; */
"Fighter" = "Cīnītājs";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1255; */
"Gunner" = "Ložmetējnieks";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1256; */
"Helm" = "Stūre";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1514; */
"cooldown {SS}s" = "Atskaite {SS}";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1626; prefs.py:306; */
"OK" = "Labi";
/* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2018; prefs.py:978; */
"Plugins" = "Spraudņi";
/* companion.py: Frontier CAPI data doesn't agree with latest Journal game location; In files: companion.py:237; */
/* companion.py: Frontier CAPI data doesn't agree with latest Journal game location; In files: companion.py:245; */
"Error: Frontier server is lagging" = "Kļūda: Frontier serveris aizkavējas";
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:263; */
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:271; */
"Error: Invalid Credentials" = "Kļūda: Nederīga lietotāja informācija";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:194; prefs.py:468; prefs.py:702; prefs.py:735; */
/* EDMarketConnector.py: Main UI Update button; EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:601; EDMarketConnector.py:919; EDMarketConnector.py:1748; */
"Update" = "Atjaunināt";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:710; prefs.py:875; */
"Always on top" = "Vienmēr virspusē";
/* EDMarketConnector.py: Label for commander name in main window; edsm.py: Game Commander name label in EDSM settings; stats.py: Cmdr stats; theme.py: Label for commander name in main window; In files: EDMarketConnector.py:913; edsm.py:332; stats.py:57; theme.py:290; */
"Cmdr" = "Cmdr";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:915; EDMarketConnector.py:1487; */
"Role" = "Loma";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:915; EDMarketConnector.py:1497; EDMarketConnector.py:1520; stats.py:405; */
"Ship" = "Kuģis";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:917; prefs.py:614; stats.py:407; */
"System" = "Sistēma";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:918; prefs.py:632; prefs.py:770; stats.py:408; */
"Station" = "Stacija";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:921; EDMarketConnector.py:939; EDMarketConnector.py:942; EDMarketConnector.py:2264; */
"File" = "Fails";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:922; EDMarketConnector.py:940; EDMarketConnector.py:943; */
"Edit" = "Labot";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:923; */
"View" = "Skats";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:924; */
"Window" = "Logs";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:925; EDMarketConnector.py:941; EDMarketConnector.py:944; */
"Help" = "Palīdzība";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:928; EDMarketConnector.py:959; EDMarketConnector.py:1804; */
"About {APP}" = "Par {APP}";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:930; EDMarketConnector.py:958; */
"Check for Updates..." = "Pārbaudīt atjauninājumus...";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:931; EDMarketConnector.py:948; */
"Save Raw Data..." = "Saglabāt sākotnējos datus...";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:932; EDMarketConnector.py:947; stats.py:402; */
"Status" = "Statuss";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:933; EDMarketConnector.py:953; */
"Documentation" = "Dokumentācija";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:936; EDMarketConnector.py:956; */
"Privacy Policy" = "Privātuma politika";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:937; EDMarketConnector.py:957; EDMarketConnector.py:1838; */
"Release Notes" = "Informācija par laidienu";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:949; EDMarketConnector.py:2265; prefs.py:241; */
"Settings" = "Iestatījumi";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:950; */
"Exit" = "Iziet";
/* EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; In files: EDMarketConnector.py:962; ttkHyperlinkLabel.py:53; */
"Copy" = "Kopēt";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:978; */
"Logging in..." = "Autorizējas...";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:994; EDMarketConnector.py:1657; */
"Authentication successful" = "Autentifikācija sekmīga";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:1025; */
"You're not docked at a station!" = "Jūs neesat stacijā!";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:1033; */
"Station doesn't have anything!" = "Stacijā nekā nav!";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:1038; */
"Station doesn't have a market!" = "Stacijā nav tirgus!";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1128; EDMarketConnector.py:1179; */
"Fetching data..." = "Iegūst datus...";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1246; stats.py:333; */
"Who are you?!" = "Kas tu esi?";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1252; stats.py:341; */
"Where are you?!" = "Kur tu esi?";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1259; stats.py:349; */
"What are you flying?!" = "Ar ko tu lido?";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1434; */
"Last updated at %H:%M:%S" = "Pēdējo reizi atjaunināts %H:%M:%S";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1462; */
"Fighter" = "Cīnītājs";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1463; */
"Gunner" = "Ložmetējnieks";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1464; */
"Helm" = "Stūre";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1742; */
"cooldown {SS}s" = "Atskaite {SS}";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1864; prefs.py:292; */
"OK" = "Labi";
/* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2263; prefs.py:986; */
"Plugins" = "Spraudņi";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:193; prefs.py:455; prefs.py:709; prefs.py:742; */
"Default" = "Noklusējuma";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:251; */
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:458; eddn.py:2413; eddn.py:2451; eddn.py:2519; */
"Error: Can't connect to EDDN" = "Kļūda: Nevar savienoties ar EDDN";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:2041; */
"Send station data to the Elite Dangerous Data Network" = "Sūtīt stacijas datus uz Elite Dangerous Data Network";
/* eddn.py: Enable EDDN support for system and other scan data checkbox label; In files: eddn.py:2052; */
"Send system and scan data to the Elite Dangerous Data Network" = "Sūtīt stacijas datus uz Elite Dangerous Data Network";
/* eddn.py: EDDN delay sending until docked option is on, this message notes that a send was skipped due to this; In files: eddn.py:2063; */
"Delay sending until docked" = "Aizkavēt sūtīšanu līdz nosēšanai";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:2507; */
"Sending data to EDDN..." = "Sūta datus uz EDDN...";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:319; */
"Elite Dangerous Star Map credentials" = "Elite Dangerous zvaigžņu kartes informācija";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:341; */
"Commander Name" = "Komandiera vārds";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:350; inara.py:278; */
"API Key" = "API atslēga";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:394; prefs.py:527; prefs.py:1157; prefs.py:1190; stats.py:154; stats.py:173; stats.py:192; stats.py:209; */
"None" = "Neviens";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:916; edsm.py:1048; */
"Error: EDSM {MSG}" = "Kļūda: EDSM {MSG}";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:953; edsm.py:1043; */
"Error: Can't connect to EDSM" = "Kļūda: Nevar savienoties ar EDSM";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:237; */
"Preferences" = "Iestatījumi";
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:348; */
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:335; */
"Please choose what data to save" = "Lūdzu izvēlaties kādus datus saglabāt";
/* prefs.py: Settings > Output option; In files: prefs.py:354; */
/* prefs.py: Settings > Output option; In files: prefs.py:341; */
"Market data in CSV format file" = "Tirgus dati CSV formātā";
/* prefs.py: Settings > Output option; In files: prefs.py:363; */
/* prefs.py: Settings > Output option; In files: prefs.py:350; */
"Market data in Trade Dangerous format file" = "Tirgus dati Trade Dangerous formātā";
/* prefs.py: Settings > Output option; In files: prefs.py:373; */
/* prefs.py: Settings > Output option; In files: prefs.py:360; */
"Ship loadout" = "Kuģa loadout";
/* prefs.py: Settings > Output option; In files: prefs.py:383; */
/* prefs.py: Settings > Output option; In files: prefs.py:370; */
"Automatically update on docking" = "Automtiski atjaunoties nosēžoties.";
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:392; prefs.py:411; */
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:379; prefs.py:398; */
"File location" = "Faila atrašanās vieta";
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:400; prefs.py:450; */
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:387; prefs.py:437; */
"Change..." = "Mainīt...";
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:403; prefs.py:453; */
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:390; prefs.py:440; */
"Browse..." = "Pārlūkot...";
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:418; */
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:405; */
"Output" = "Izvade";
/* prefs.py: Settings > Configuration - Label for Journal files location; In files: prefs.py:444; prefs.py:459; */
/* prefs.py: Settings > Configuration - Label for Journal files location; In files: prefs.py:431; prefs.py:446; */
"E:D journal file location" = "E:D žurnāla faila atrašanās vieta";
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:484; */
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:490; */
"Keyboard shortcut" = "Ātrais taustiņš";
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:486; */
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:492; */
"Hotkey" = "Ātrais taustiņš";
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:495; */
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:501; */
"Re-start {APP} to use shortcuts" = "Restartējiet {APP} lai izmantotu īssceļus";
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:504; */
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:510; */
"{APP} needs permission to use shortcuts" = "{APP} vajag atļaujas lai izmantotu īssceļus";
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:509; */
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:515; */
"Open System Preferences" = "Atvērt sistēmas iestatījumus";
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:531; */
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:538; */
"Only when Elite: Dangerous is the active app" = "Tikai kad Elite: Dangerous ir aktīvā aplikācija";
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:542; */
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:549; */
"Play sound" = "Atskaņot skaņu";
/* prefs.py: Label for Shipyard provider selection; In files: prefs.py:581; */
/* prefs.py: Label for Shipyard provider selection; In files: prefs.py:588; */
"Shipyard" = "Kuģu būvētava";
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:674; */
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:681; */
"Configuration" = "Konfigurācija";
/* prefs.py: Preferences privacy tab title; In files: prefs.py:696; */
/* prefs.py: Preferences privacy tab title; In files: prefs.py:703; */
"Privacy" = "Privātums";
/* prefs.py: Label for Settings > Appeareance > selection of 'normal' text colour; In files: prefs.py:709; */
/* prefs.py: Label for Settings > Appeareance > selection of 'normal' text colour; In files: prefs.py:716; */
"Normal text" = "Normālais teksts";
/* prefs.py: Label for Settings > Appeareance > selection of 'highlightes' text colour; In files: prefs.py:711; */
/* prefs.py: Label for Settings > Appeareance > selection of 'highlightes' text colour; In files: prefs.py:718; */
"Highlighted text" = "Izceltais teksts";
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:720; */
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:727; */
"Language" = "Valoda";
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:730; */
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:737; */
"Theme" = "Dizains";
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:741; */
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:749; */
"Dark" = "Tumša";
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:748; */
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:756; */
"Transparent" = "Caurspīdīga";
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:885; */
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:893; */
"Appearance" = "Izskats";
/* prefs.py: Label for location of third-party plugins folder; In files: prefs.py:900; */
/* prefs.py: Label for location of third-party plugins folder; In files: prefs.py:908; */
"Plugins folder" = "Spraudņu mape";
/* prefs.py: Label on button used to open a filesystem folder; In files: prefs.py:907; */
/* prefs.py: Label on button used to open a filesystem folder; In files: prefs.py:915; */
"Open" = "Atvērt";
/* prefs.py: Label on list of enabled plugins; In files: prefs.py:926; */
/* prefs.py: Label on list of enabled plugins; In files: prefs.py:934; */
"Enabled Plugins" = "Iespējoti Spraudņi";
/* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:969; */
/* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:977; */
"Disabled Plugins" = "Atspējoti Spraudņi";
/* stats.py: Cmdr stats; In files: stats.py:60; */
/* stats.py: Cmdr stats; In files: stats.py:58; */
"Balance" = "Bilance";
/* stats.py: Cmdr stats; In files: stats.py:61; */
/* stats.py: Cmdr stats; In files: stats.py:59; */
"Loan" = "Aizdevums";
/* stats.py: Top rank; In files: stats.py:65; */
/* stats.py: Top rank; In files: stats.py:63; */
"Elite" = "Elite";
/* stats.py: Ranking; In files: stats.py:76; */
/* stats.py: Ranking; In files: stats.py:74; */
"Combat" = "Combat";
/* stats.py: Ranking; In files: stats.py:77; */
/* stats.py: Ranking; In files: stats.py:75; */
"Trade" = "Trade";
/* stats.py: Ranking; In files: stats.py:78; */
/* stats.py: Ranking; In files: stats.py:76; */
"Explorer" = "Explorer";
/* stats.py: Ranking; In files: stats.py:81; */
/* stats.py: Ranking; In files: stats.py:79; */
"CQC" = "CQC";
/* stats.py: Ranking; In files: stats.py:82; */
/* stats.py: Ranking; In files: stats.py:80; */
"Federation" = "Federation";
/* stats.py: Ranking; In files: stats.py:83; */
/* stats.py: Ranking; In files: stats.py:81; */
"Empire" = "Empire";
/* stats.py: Ranking; In files: stats.py:84; */
/* stats.py: Ranking; In files: stats.py:82; */
"Powerplay" = "Powerplay";
/* stats.py: Combat rank; In files: stats.py:93; */
/* stats.py: Combat rank; In files: stats.py:91; */
"Harmless" = "Nekaitīgs";
/* stats.py: Combat rank; In files: stats.py:94; */
/* stats.py: Combat rank; In files: stats.py:92; */
"Mostly Harmless" = "Mostly Harmless";
/* stats.py: Combat rank; In files: stats.py:95; */
/* stats.py: Combat rank; In files: stats.py:93; */
"Novice" = "Novice";
/* stats.py: Combat rank; In files: stats.py:96; */
/* stats.py: Combat rank; In files: stats.py:94; */
"Competent" = "Competent";
/* stats.py: Combat rank; In files: stats.py:97; */
/* stats.py: Combat rank; In files: stats.py:95; */
"Expert" = "Expert";
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:98; stats.py:178; */
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:96; stats.py:176; */
"Master" = "Master";
/* stats.py: Combat rank; In files: stats.py:99; */
/* stats.py: Combat rank; In files: stats.py:97; */
"Dangerous" = "Dangerous";
/* stats.py: Combat rank; In files: stats.py:100; */
/* stats.py: Combat rank; In files: stats.py:98; */
"Deadly" = "Deadly";
/* stats.py: Trade rank; In files: stats.py:103; */
/* stats.py: Trade rank; In files: stats.py:101; */
"Penniless" = "Penniless";
/* stats.py: Trade rank; In files: stats.py:104; */
/* stats.py: Trade rank; In files: stats.py:102; */
"Mostly Penniless" = "Mostly Penniless";
/* stats.py: Trade rank; In files: stats.py:105; */
/* stats.py: Trade rank; In files: stats.py:103; */
"Peddler" = "Peddler";
/* stats.py: Trade rank; In files: stats.py:106; */
/* stats.py: Trade rank; In files: stats.py:104; */
"Dealer" = "Dealer";
/* stats.py: Trade rank; In files: stats.py:107; */
/* stats.py: Trade rank; In files: stats.py:105; */
"Merchant" = "Merchant";
/* stats.py: Trade rank; In files: stats.py:108; */
/* stats.py: Trade rank; In files: stats.py:106; */
"Broker" = "Broker";
/* stats.py: Trade rank; In files: stats.py:109; */
/* stats.py: Trade rank; In files: stats.py:107; */
"Entrepreneur" = "Entrepreneur";
/* stats.py: Trade rank; In files: stats.py:110; */
/* stats.py: Trade rank; In files: stats.py:108; */
"Tycoon" = "Tycoon";
/* stats.py: Explorer rank; In files: stats.py:113; */
/* stats.py: Explorer rank; In files: stats.py:111; */
"Aimless" = "Aimless";
/* stats.py: Explorer rank; In files: stats.py:114; */
/* stats.py: Explorer rank; In files: stats.py:112; */
"Mostly Aimless" = "Mostly Aimless";
/* stats.py: Explorer rank; In files: stats.py:115; */
/* stats.py: Explorer rank; In files: stats.py:113; */
"Scout" = "Scout";
/* stats.py: Explorer rank; In files: stats.py:116; */
/* stats.py: Explorer rank; In files: stats.py:114; */
"Surveyor" = "Surveyor";
/* stats.py: Explorer rank; In files: stats.py:117; */
/* stats.py: Explorer rank; In files: stats.py:115; */
"Trailblazer" = "Trailblazer";
/* stats.py: Explorer rank; In files: stats.py:118; */
/* stats.py: Explorer rank; In files: stats.py:116; */
"Pathfinder" = "Pathfinder";
/* stats.py: Explorer rank; In files: stats.py:119; */
/* stats.py: Explorer rank; In files: stats.py:117; */
"Ranger" = "Ranger";
/* stats.py: Explorer rank; In files: stats.py:120; */
/* stats.py: Explorer rank; In files: stats.py:118; */
"Pioneer" = "Pioneer";
/* stats.py: CQC rank; In files: stats.py:144; */
/* stats.py: CQC rank; In files: stats.py:142; */
"Helpless" = "Bezpalīdzīgs";
/* stats.py: CQC rank; In files: stats.py:145; */
/* stats.py: CQC rank; In files: stats.py:143; */
"Mostly Helpless" = "Mostly Helpless";
/* stats.py: CQC rank; In files: stats.py:146; */
/* stats.py: CQC rank; In files: stats.py:144; */
"Amateur" = "Amateur";
/* stats.py: CQC rank; In files: stats.py:147; */
/* stats.py: CQC rank; In files: stats.py:145; */
"Semi Professional" = "Semi Professional";
/* stats.py: CQC rank; In files: stats.py:148; */
/* stats.py: CQC rank; In files: stats.py:146; */
"Professional" = "Professional";
/* stats.py: CQC rank; In files: stats.py:149; */
/* stats.py: CQC rank; In files: stats.py:147; */
"Champion" = "Champion";
/* stats.py: CQC rank; In files: stats.py:150; */
/* stats.py: CQC rank; In files: stats.py:148; */
"Hero" = "Varonis";
/* stats.py: CQC rank; In files: stats.py:151; */
/* stats.py: CQC rank; In files: stats.py:149; */
"Gladiator" = "Gladiators";
/* stats.py: Federation rank; In files: stats.py:157; */
/* stats.py: Federation rank; In files: stats.py:155; */
"Recruit" = "Recruit";
/* stats.py: Federation rank; In files: stats.py:158; */
/* stats.py: Federation rank; In files: stats.py:156; */
"Cadet" = "Cadet";
/* stats.py: Federation rank; In files: stats.py:159; */
/* stats.py: Federation rank; In files: stats.py:157; */
"Midshipman" = "Midshipman";
/* stats.py: Federation rank; In files: stats.py:160; */
/* stats.py: Federation rank; In files: stats.py:158; */
"Petty Officer" = "Petty Officer";
/* stats.py: Federation rank; In files: stats.py:161; */
/* stats.py: Federation rank; In files: stats.py:159; */
"Chief Petty Officer" = "Chief Petty Officer";
/* stats.py: Federation rank; In files: stats.py:162; */
/* stats.py: Federation rank; In files: stats.py:160; */
"Warrant Officer" = "Warrant Officer";
/* stats.py: Federation rank; In files: stats.py:163; */
/* stats.py: Federation rank; In files: stats.py:161; */
"Ensign" = "Ensign";
/* stats.py: Federation rank; In files: stats.py:164; */
/* stats.py: Federation rank; In files: stats.py:162; */
"Lieutenant" = "Leitnants";
/* stats.py: Federation rank; In files: stats.py:165; */
/* stats.py: Federation rank; In files: stats.py:163; */
"Lieutenant Commander" = "Lieutenant Commander";
/* stats.py: Federation rank; In files: stats.py:166; */
/* stats.py: Federation rank; In files: stats.py:164; */
"Post Commander" = "Post Commander";
/* stats.py: Federation rank; In files: stats.py:167; */
/* stats.py: Federation rank; In files: stats.py:165; */
"Post Captain" = "Post Captain";
/* stats.py: Federation rank; In files: stats.py:168; */
/* stats.py: Federation rank; In files: stats.py:166; */
"Rear Admiral" = "Rear Admiral";
/* stats.py: Federation rank; In files: stats.py:169; */
/* stats.py: Federation rank; In files: stats.py:167; */
"Vice Admiral" = "Vice Admiral";
/* stats.py: Federation rank; In files: stats.py:170; */
/* stats.py: Federation rank; In files: stats.py:168; */
"Admiral" = "Admiral";
/* stats.py: Empire rank; In files: stats.py:176; */
/* stats.py: Empire rank; In files: stats.py:174; */
"Outsider" = "Outsider";
/* stats.py: Empire rank; In files: stats.py:177; */
/* stats.py: Empire rank; In files: stats.py:175; */
"Serf" = "Serf";
/* stats.py: Empire rank; In files: stats.py:179; */
/* stats.py: Empire rank; In files: stats.py:177; */
"Squire" = "Squire";
/* stats.py: Empire rank; In files: stats.py:180; */
/* stats.py: Empire rank; In files: stats.py:178; */
"Knight" = "Bruņinieks";
/* stats.py: Empire rank; In files: stats.py:181; */
/* stats.py: Empire rank; In files: stats.py:179; */
"Lord" = "Lord";
/* stats.py: Empire rank; In files: stats.py:182; */
/* stats.py: Empire rank; In files: stats.py:180; */
"Baron" = "Baron";
/* stats.py: Empire rank; In files: stats.py:183; */
/* stats.py: Empire rank; In files: stats.py:181; */
"Viscount" = "Viscount";
/* stats.py: Empire rank; In files: stats.py:184; */
/* stats.py: Empire rank; In files: stats.py:182; */
"Count" = "Count";
/* stats.py: Empire rank; In files: stats.py:185; */
/* stats.py: Empire rank; In files: stats.py:183; */
"Earl" = "Earl";
/* stats.py: Empire rank; In files: stats.py:186; */
/* stats.py: Empire rank; In files: stats.py:184; */
"Marquis" = "Marquis";
/* stats.py: Empire rank; In files: stats.py:187; */
/* stats.py: Empire rank; In files: stats.py:185; */
"Duke" = "Duke";
/* stats.py: Empire rank; In files: stats.py:188; */
/* stats.py: Empire rank; In files: stats.py:186; */
"Prince" = "Prince";
/* stats.py: Empire rank; In files: stats.py:189; */
/* stats.py: Empire rank; In files: stats.py:187; */
"King" = "Karalis";
/* stats.py: Power rank; In files: stats.py:195; */
/* stats.py: Power rank; In files: stats.py:193; */
"Rating 1" = "Rating 1";
/* stats.py: Power rank; In files: stats.py:196; */
/* stats.py: Power rank; In files: stats.py:194; */
"Rating 2" = "Rating 2";
/* stats.py: Power rank; In files: stats.py:197; */
/* stats.py: Power rank; In files: stats.py:195; */
"Rating 3" = "Rating 3";
/* stats.py: Power rank; In files: stats.py:198; */
/* stats.py: Power rank; In files: stats.py:196; */
"Rating 4" = "Rating 4";
/* stats.py: Power rank; In files: stats.py:199; */
/* stats.py: Power rank; In files: stats.py:197; */
"Rating 5" = "Rating 5";
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:413; */
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:409; */
"Value" = "Vērtība";
/* stats.py: Status dialog title; In files: stats.py:422; */
/* stats.py: Status dialog title; In files: stats.py:418; */
"Ships" = "Kuģi";
+282 -282
View File
@@ -1,518 +1,518 @@
/* Language name */
"!Language" = "Nederlands";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:212; eddn.py:584; eddn.py:925; load.py:215; load.py:593; load.py:940; eddn.py:252; eddn.py:699; eddn.py:1439; */
"Sending data to EDDN..." = "Bezig data te versturen naar EDDN...";
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:257; eddn.py:864; eddn.py:898; eddn.py:937; load.py:260; load.py:878; load.py:913; load.py:952; eddn.py:316; eddn.py:1375; eddn.py:1410; eddn.py:1451; */
"Error: Can't connect to EDDN" = "Fout: Kan geen verbinding maken met EDDN";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:663; load.py:672; eddn.py:1122; */
"Send station data to the Elite Dangerous Data Network" = "Stuur station gegevens naar het Elite Dangerous Data Network";
/* eddn.py: Enable EDDN support for system and other scan data checkbox label; In files: eddn.py:673; load.py:682; eddn.py:1133; */
"Send system and scan data to the Elite Dangerous Data Network" = "Stuur systeem en scan gegevens naar het Elite Dangerous Data Network";
/* eddn.py: EDDN delay sending until docked option is on, this message notes that a send was skipped due to this; In files: eddn.py:683; load.py:692; eddn.py:1144; */
"Delay sending until docked" = "Stel verzending uit tot na het docken";
/* edsm.py: Settings>EDSM - Label on checkbox for 'send data'; In files: edsm.py:197; load.py:197; edsm.py:237; */
"Send flight log and Cmdr status to EDSM" = "Stuur vlucht logboek en Cmdr status naar EDSM";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:206; load.py:206; edsm.py:247; */
"Elite Dangerous Star Map credentials" = "Elite Dangerous Star Map inlog gegevens";
/* edsm.py: Game Commander name label in EDSM settings; theme.py: Label for commander name in main window; EDMarketConnector.py: Label for commander name in main window; stats.py: Cmdr stats; In files: edsm.py:216; load.py:216; edsm.py:258; theme.py:227; EDMarketConnector.py:822; stats.py:59; */
"Cmdr" = "Cmdr";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:223; load.py:223; edsm.py:266; */
"Commander Name" = "Commander naam";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:230; inara.py:233; load.py:230; load.py:233; edsm.py:274; inara.py:243; */
"API Key" = "API Key";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:256; load.py:256; edsm.py:301; prefs.py:520; prefs.py:1174; prefs.py:1207; stats.py:156; stats.py:175; stats.py:194; stats.py:211; */
"None" = "Geen";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:622; edsm.py:724; load.py:627; load.py:729; edsm.py:747; edsm.py:875; */
"Error: EDSM {MSG}" = "Fout: EDSM {MSG}";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:658; edsm.py:720; load.py:663; load.py:725; edsm.py:784; edsm.py:870; */
"Error: Can't connect to EDSM" = "Fout: Kan geen verbinding maken met EDSM";
/* inara.py: Checkbox to enable INARA API Usage; In files: inara.py:215; load.py:215; inara.py:222; */
"Send flight log and Cmdr status to Inara" = "Stuur vlucht logboek en Cmdr status naar Inara";
/* inara.py: Text for INARA API keys link ( goes to https://inara.cz/settings-api ); In files: inara.py:225; load.py:225; inara.py:234; */
"Inara credentials" = "Inara inlog gegevens";
/* inara.py: INARA API returned some kind of error (error message will be contained in {MSG}); In files: inara.py:1316; inara.py:1328; load.py:1319; load.py:1331; inara.py:1587; inara.py:1600; */
"Error: Inara {MSG}" = "Fout: Inara {MSG}";
/* ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; In files: ttkHyperlinkLabel.py:42; EDMarketConnector.py:866; */
"Copy" = "Kopieëren";
/* EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:529; EDMarketConnector.py:828; EDMarketConnector.py:1520; */
"Update" = "Bijwerken";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:612; prefs.py:867; */
"Always on top" = "Altijd op voorgrond";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:824; EDMarketConnector.py:1279; */
"Role" = "Rol";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:824; EDMarketConnector.py:1289; EDMarketConnector.py:1312; stats.py:409; */
"Ship" = "Schip";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:826; prefs.py:607; stats.py:411; */
"System" = "Systeem";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:827; prefs.py:625; prefs.py:762; stats.py:412; */
"Station" = "Station";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:830; EDMarketConnector.py:845; EDMarketConnector.py:848; EDMarketConnector.py:2019; */
"File" = "Bestand";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:831; EDMarketConnector.py:846; EDMarketConnector.py:849; */
"Edit" = "Bewerken";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:832; */
"View" = "Beeld";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:833; */
"Window" = "Venster";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:834; EDMarketConnector.py:847; EDMarketConnector.py:850; */
"Help" = "Help";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:837; EDMarketConnector.py:863; EDMarketConnector.py:1566; */
"About {APP}" = "Over {APP}";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:839; EDMarketConnector.py:862; */
"Check for Updates..." = "Controleren op updates...";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:840; EDMarketConnector.py:854; */
"Save Raw Data..." = "Sla onbewerkte gegevens op...";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:841; EDMarketConnector.py:853; stats.py:406; */
"Status" = "Status";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:842; EDMarketConnector.py:860; */
"Privacy Policy" = "Privacybeleid";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:843; EDMarketConnector.py:861; EDMarketConnector.py:1600; */
"Release Notes" = "Release opmerkingen";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:855; EDMarketConnector.py:2020; prefs.py:255; */
"Settings" = "Instellingen";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:856; */
"Exit" = "Afsluiten";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:859; */
"Documentation" = "Documentatie";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:872; */
"Logging in..." = "Bezig met inloggen...";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:888; EDMarketConnector.py:1431; */
"Authentication successful" = "Authenticatie voltooid";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:919; */
"You're not docked at a station!" = "Je bent niet gedockt in een station!";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:927; */
"Station doesn't have anything!" = "Station heeft helemaal niets!";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:932; */
"Station doesn't have a market!" = "Station heeft geen markt!";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1006; */
"Fetching data..." = "Data wordt opgehaald...";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1051; stats.py:335; */
"Who are you?!" = "Wie ben je?!";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1057; stats.py:345; */
"Where are you?!" = "Waar ben je?!";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1064; stats.py:353; */
"What are you flying?!" = "Waar vlieg je in?!";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1226; */
"Last updated at %H:%M:%S" = "Voor het laatst bijgewerkt om %H:%M:%S";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1254; */
"Fighter" = "Gevechtsvlieger";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1255; */
"Gunner" = "Schutter";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1256; */
"Helm" = "Stuurman";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1514; */
"cooldown {SS}s" = "wachtperiode {SS}s";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1626; prefs.py:306; */
"OK" = "OK";
/* EDMarketConnector.py: Popup-text about 'active' plugins without Python 3.x support; In files: EDMarketConnector.py:2008:2014; */
"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." = "Een of meer ingeschakelde plugins hebben nog geen Python 3.x ondersteuning. Kijk op de lijst op de '{PLUGINS}' tab van '{FILE}' > '{SETTINGS}'. Check of er een bijgewerkte versie beschikbaar is. Laat anders de developer weten dat ze de code moeten updaten voor Python 3.x.\n\nJe kan de plugin uitschakelen door het mapje te hernoemen met '{DISABLED}' aan het eind van de naam.";
/* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2018; prefs.py:978; */
"Plugins" = "Plugins";
/* EDMarketConnector.py: Popup window title for list of 'enabled' plugins that don't work with Python 3.x; In files: EDMarketConnector.py:2029; */
"EDMC: Plugins Without Python 3.x Support" = "EDMS: Plugins zonder Python 3.x ondersteuning";
/* companion.py: Frontier CAPI data doesn't agree with latest Journal game location; In files: companion.py:237; */
/* companion.py: Frontier CAPI data doesn't agree with latest Journal game location; In files: companion.py:245; */
"Error: Frontier server is lagging" = "Fout: Frontier server is vertraagd";
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:263; */
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:271; */
"Error: Invalid Credentials" = "Fout: Ongeldige inlog gegevens";
/* companion.py: Frontier CAPI authorisation not for currently game-active commander; In files: companion.py:288; */
/* companion.py: Frontier CAPI authorisation not for currently game-active commander; In files: companion.py:296; */
"Error: Wrong Cmdr" = "Fout: Verkeerde Cmdr";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:194; prefs.py:468; prefs.py:702; prefs.py:735; */
/* EDMarketConnector.py: Main UI Update button; EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:601; EDMarketConnector.py:919; EDMarketConnector.py:1748; */
"Update" = "Bijwerken";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:710; prefs.py:875; */
"Always on top" = "Altijd op voorgrond";
/* EDMarketConnector.py: Label for commander name in main window; edsm.py: Game Commander name label in EDSM settings; stats.py: Cmdr stats; theme.py: Label for commander name in main window; In files: EDMarketConnector.py:913; edsm.py:332; stats.py:57; theme.py:290; */
"Cmdr" = "Cmdr";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:915; EDMarketConnector.py:1487; */
"Role" = "Rol";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:915; EDMarketConnector.py:1497; EDMarketConnector.py:1520; stats.py:405; */
"Ship" = "Schip";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:917; prefs.py:614; stats.py:407; */
"System" = "Systeem";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:918; prefs.py:632; prefs.py:770; stats.py:408; */
"Station" = "Station";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:921; EDMarketConnector.py:939; EDMarketConnector.py:942; EDMarketConnector.py:2264; */
"File" = "Bestand";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:922; EDMarketConnector.py:940; EDMarketConnector.py:943; */
"Edit" = "Bewerken";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:923; */
"View" = "Beeld";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:924; */
"Window" = "Venster";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:925; EDMarketConnector.py:941; EDMarketConnector.py:944; */
"Help" = "Help";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:928; EDMarketConnector.py:959; EDMarketConnector.py:1804; */
"About {APP}" = "Over {APP}";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:930; EDMarketConnector.py:958; */
"Check for Updates..." = "Controleren op updates...";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:931; EDMarketConnector.py:948; */
"Save Raw Data..." = "Sla onbewerkte gegevens op...";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:932; EDMarketConnector.py:947; stats.py:402; */
"Status" = "Status";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:933; EDMarketConnector.py:953; */
"Documentation" = "Documentatie";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:936; EDMarketConnector.py:956; */
"Privacy Policy" = "Privacybeleid";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:937; EDMarketConnector.py:957; EDMarketConnector.py:1838; */
"Release Notes" = "Release opmerkingen";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:949; EDMarketConnector.py:2265; prefs.py:241; */
"Settings" = "Instellingen";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:950; */
"Exit" = "Afsluiten";
/* EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; In files: EDMarketConnector.py:962; ttkHyperlinkLabel.py:53; */
"Copy" = "Kopieëren";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:978; */
"Logging in..." = "Bezig met inloggen...";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:994; EDMarketConnector.py:1657; */
"Authentication successful" = "Authenticatie voltooid";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:1025; */
"You're not docked at a station!" = "Je bent niet gedockt in een station!";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:1033; */
"Station doesn't have anything!" = "Station heeft helemaal niets!";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:1038; */
"Station doesn't have a market!" = "Station heeft geen markt!";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1128; EDMarketConnector.py:1179; */
"Fetching data..." = "Data wordt opgehaald...";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1246; stats.py:333; */
"Who are you?!" = "Wie ben je?!";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1252; stats.py:341; */
"Where are you?!" = "Waar ben je?!";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1259; stats.py:349; */
"What are you flying?!" = "Waar vlieg je in?!";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1434; */
"Last updated at %H:%M:%S" = "Voor het laatst bijgewerkt om %H:%M:%S";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1462; */
"Fighter" = "Gevechtsvlieger";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1463; */
"Gunner" = "Schutter";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1464; */
"Helm" = "Stuurman";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1742; */
"cooldown {SS}s" = "wachtperiode {SS}s";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1864; prefs.py:292; */
"OK" = "OK";
/* EDMarketConnector.py: Popup-text about 'active' plugins without Python 3.x support; In files: EDMarketConnector.py:2253:2259; */
"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." = "Een of meer ingeschakelde plugins hebben nog geen Python 3.x ondersteuning. Kijk op de lijst op de '{PLUGINS}' tab van '{FILE}' > '{SETTINGS}'. Check of er een bijgewerkte versie beschikbaar is. Laat anders de developer weten dat ze de code moeten updaten voor Python 3.x.\n\nJe kan de plugin uitschakelen door het mapje te hernoemen met '{DISABLED}' aan het eind van de naam.";
/* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2263; prefs.py:986; */
"Plugins" = "Plugins";
/* EDMarketConnector.py: Popup window title for list of 'enabled' plugins that don't work with Python 3.x; In files: EDMarketConnector.py:2274; */
"EDMC: Plugins Without Python 3.x Support" = "EDMS: Plugins zonder Python 3.x ondersteuning";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:193; prefs.py:455; prefs.py:709; prefs.py:742; */
"Default" = "Standaard";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:251; */
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:458; eddn.py:2413; eddn.py:2451; eddn.py:2519; */
"Error: Can't connect to EDDN" = "Fout: Kan geen verbinding maken met EDDN";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:2041; */
"Send station data to the Elite Dangerous Data Network" = "Stuur station gegevens naar het Elite Dangerous Data Network";
/* eddn.py: Enable EDDN support for system and other scan data checkbox label; In files: eddn.py:2052; */
"Send system and scan data to the Elite Dangerous Data Network" = "Stuur systeem en scan gegevens naar het Elite Dangerous Data Network";
/* eddn.py: EDDN delay sending until docked option is on, this message notes that a send was skipped due to this; In files: eddn.py:2063; */
"Delay sending until docked" = "Stel verzending uit tot na het docken";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:2507; */
"Sending data to EDDN..." = "Bezig data te versturen naar EDDN...";
/* edsm.py: Settings>EDSM - Label on checkbox for 'send data'; In files: edsm.py:308; */
"Send flight log and Cmdr status to EDSM" = "Stuur vlucht logboek en Cmdr status naar EDSM";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:319; */
"Elite Dangerous Star Map credentials" = "Elite Dangerous Star Map inlog gegevens";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:341; */
"Commander Name" = "Commander naam";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:350; inara.py:278; */
"API Key" = "API Key";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:394; prefs.py:527; prefs.py:1157; prefs.py:1190; stats.py:154; stats.py:173; stats.py:192; stats.py:209; */
"None" = "Geen";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:916; edsm.py:1048; */
"Error: EDSM {MSG}" = "Fout: EDSM {MSG}";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:953; edsm.py:1043; */
"Error: Can't connect to EDSM" = "Fout: Kan geen verbinding maken met EDSM";
/* inara.py: Checkbox to enable INARA API Usage; In files: inara.py:257; */
"Send flight log and Cmdr status to Inara" = "Stuur vlucht logboek en Cmdr status naar Inara";
/* inara.py: Text for INARA API keys link ( goes to https://inara.cz/settings-api ); In files: inara.py:269; */
"Inara credentials" = "Inara inlog gegevens";
/* inara.py: INARA API returned some kind of error (error message will be contained in {MSG}); In files: inara.py:1650; inara.py:1663; */
"Error: Inara {MSG}" = "Fout: Inara {MSG}";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:237; */
"Preferences" = "Preferences";
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:348; */
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:335; */
"Please choose what data to save" = "Welke gegevens wilt u opslaan?";
/* prefs.py: Settings > Output option; In files: prefs.py:354; */
/* prefs.py: Settings > Output option; In files: prefs.py:341; */
"Market data in CSV format file" = "Markt data in CSV bestand formaat";
/* prefs.py: Settings > Output option; In files: prefs.py:363; */
/* prefs.py: Settings > Output option; In files: prefs.py:350; */
"Market data in Trade Dangerous format file" = "Markt data in Trade Dangerous bestand formaat";
/* prefs.py: Settings > Output option; In files: prefs.py:373; */
/* prefs.py: Settings > Output option; In files: prefs.py:360; */
"Ship loadout" = "Schip inrichting";
/* prefs.py: Settings > Output option; In files: prefs.py:383; */
/* prefs.py: Settings > Output option; In files: prefs.py:370; */
"Automatically update on docking" = "Automatisch bijwerken na de landing";
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:392; prefs.py:411; */
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:379; prefs.py:398; */
"File location" = "Bestand locatie";
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:400; prefs.py:450; */
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:387; prefs.py:437; */
"Change..." = "Wijzigen...";
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:403; prefs.py:453; */
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:390; prefs.py:440; */
"Browse..." = "Bladeren...";
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:418; */
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:405; */
"Output" = "Uitvoer";
/* prefs.py: Settings > Configuration - Label for Journal files location; In files: prefs.py:444; prefs.py:459; */
/* prefs.py: Settings > Configuration - Label for Journal files location; In files: prefs.py:431; prefs.py:446; */
"E:D journal file location" = "E:D journaal bestand locatie";
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:484; */
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:490; */
"Keyboard shortcut" = "Toetscombinaties";
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:486; */
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:492; */
"Hotkey" = "Hotkey";
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:495; */
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:501; */
"Re-start {APP} to use shortcuts" = "{APP} heropstarten om toetscombinaties te gebruiken";
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:504; */
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:510; */
"{APP} needs permission to use shortcuts" = "{APP} heeft toestemming nodig om toetscombinaties te gebruiken";
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:509; */
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:515; */
"Open System Preferences" = "Open Systeemvoorkeuren";
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:531; */
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:538; */
"Only when Elite: Dangerous is the active app" = "Alleen wanneer Elite: Dangerous de actieve applicatie is";
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:542; */
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:549; */
"Play sound" = "Geluid afspelen";
/* prefs.py: Configuration - disable checks for app updates when in-game; In files: prefs.py:557; */
/* prefs.py: Configuration - disable checks for app updates when in-game; In files: prefs.py:564; */
"Disable Automatic Application Updates Check when in-game" = "Schakel Automatische update check uit wanneer in-game";
/* prefs.py: Label for preferred shipyard, system and station 'providers'; In files: prefs.py:570; */
/* prefs.py: Label for preferred shipyard, system and station 'providers'; In files: prefs.py:577; */
"Preferred websites" = "Voorkeur websites";
/* prefs.py: Label for Shipyard provider selection; In files: prefs.py:581; */
/* prefs.py: Label for Shipyard provider selection; In files: prefs.py:588; */
"Shipyard" = "Scheepswerf";
/* prefs.py: Label for checkbox to utilise alternative Coriolis URL method; In files: prefs.py:593; */
/* prefs.py: Label for checkbox to utilise alternative Coriolis URL method; In files: prefs.py:600; */
"Use alternate URL method" = "Gebruik andere URL methode";
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:674; */
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:681; */
"Configuration" = "Configuratie";
/* prefs.py: Preferences privacy tab title; In files: prefs.py:696; */
/* prefs.py: Preferences privacy tab title; In files: prefs.py:703; */
"Privacy" = "Privacy";
/* prefs.py: Label for Settings > Appeareance > selection of 'normal' text colour; In files: prefs.py:709; */
/* prefs.py: Label for Settings > Appeareance > selection of 'normal' text colour; In files: prefs.py:716; */
"Normal text" = "Normale tekst";
/* prefs.py: Label for Settings > Appeareance > selection of 'highlightes' text colour; In files: prefs.py:711; */
/* prefs.py: Label for Settings > Appeareance > selection of 'highlightes' text colour; In files: prefs.py:718; */
"Highlighted text" = "Gemarkeerde tekst";
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:720; */
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:727; */
"Language" = "Taal";
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:730; */
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:737; */
"Theme" = "Thema";
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:741; */
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:749; */
"Dark" = "Donker";
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:748; */
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:756; */
"Transparent" = "Transparent";
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:885; */
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:893; */
"Appearance" = "Vormgeving";
/* prefs.py: Label for location of third-party plugins folder; In files: prefs.py:900; */
/* prefs.py: Label for location of third-party plugins folder; In files: prefs.py:908; */
"Plugins folder" = "Plugins map";
/* prefs.py: Label on button used to open a filesystem folder; In files: prefs.py:907; */
/* prefs.py: Label on button used to open a filesystem folder; In files: prefs.py:915; */
"Open" = "Openen";
/* prefs.py: Tip/label about how to disable plugins; In files: prefs.py:915; */
/* prefs.py: Tip/label about how to disable plugins; In files: prefs.py:923; */
"Tip: You can disable a plugin by{CR}adding '{EXT}' to its folder name" = "Tip: Je kan een plugin deactiveren door '{EXT}'{CR}toe te voegen aan de map naam";
/* prefs.py: Label on list of enabled plugins; In files: prefs.py:926; */
/* prefs.py: Label on list of enabled plugins; In files: prefs.py:934; */
"Enabled Plugins" = "Geactiveerde plugins";
/* prefs.py: Plugins - Label on URL to documentation about migrating plugins from Python 2.7; In files: prefs.py:954; */
/* prefs.py: Plugins - Label on URL to documentation about migrating plugins from Python 2.7; In files: prefs.py:962; */
"Information on migrating plugins" = "Informatie over plugins migreren";
/* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:969; */
/* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:977; */
"Disabled Plugins" = "Gedeactiveerde plugins";
/* stats.py: Cmdr stats; In files: stats.py:60; */
/* stats.py: Cmdr stats; In files: stats.py:58; */
"Balance" = "Balans";
/* stats.py: Cmdr stats; In files: stats.py:61; */
/* stats.py: Cmdr stats; In files: stats.py:59; */
"Loan" = "Lening";
/* stats.py: Top rank; In files: stats.py:65; */
/* stats.py: Top rank; In files: stats.py:63; */
"Elite" = "Elite";
/* stats.py: Ranking; In files: stats.py:76; */
/* stats.py: Ranking; In files: stats.py:74; */
"Combat" = "Gevechts rang";
/* stats.py: Ranking; In files: stats.py:77; */
/* stats.py: Ranking; In files: stats.py:75; */
"Trade" = "Handels rang";
/* stats.py: Ranking; In files: stats.py:78; */
/* stats.py: Ranking; In files: stats.py:76; */
"Explorer" = "Ontdekkingsreiziger rang";
/* stats.py: Ranking; In files: stats.py:81; */
/* stats.py: Ranking; In files: stats.py:79; */
"CQC" = "CQC";
/* stats.py: Ranking; In files: stats.py:82; */
/* stats.py: Ranking; In files: stats.py:80; */
"Federation" = "Federation";
/* stats.py: Ranking; In files: stats.py:83; */
/* stats.py: Ranking; In files: stats.py:81; */
"Empire" = "Empire";
/* stats.py: Ranking; In files: stats.py:84; */
/* stats.py: Ranking; In files: stats.py:82; */
"Powerplay" = "Powerplay";
/* stats.py: Combat rank; In files: stats.py:93; */
/* stats.py: Combat rank; In files: stats.py:91; */
"Harmless" = "Harmless";
/* stats.py: Combat rank; In files: stats.py:94; */
/* stats.py: Combat rank; In files: stats.py:92; */
"Mostly Harmless" = "Mostly Harmless";
/* stats.py: Combat rank; In files: stats.py:95; */
/* stats.py: Combat rank; In files: stats.py:93; */
"Novice" = "Novice";
/* stats.py: Combat rank; In files: stats.py:96; */
/* stats.py: Combat rank; In files: stats.py:94; */
"Competent" = "Competent";
/* stats.py: Combat rank; In files: stats.py:97; */
/* stats.py: Combat rank; In files: stats.py:95; */
"Expert" = "Expert";
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:98; stats.py:178; */
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:96; stats.py:176; */
"Master" = "Master";
/* stats.py: Combat rank; In files: stats.py:99; */
/* stats.py: Combat rank; In files: stats.py:97; */
"Dangerous" = "Dangerous";
/* stats.py: Combat rank; In files: stats.py:100; */
/* stats.py: Combat rank; In files: stats.py:98; */
"Deadly" = "Deadly";
/* stats.py: Trade rank; In files: stats.py:103; */
/* stats.py: Trade rank; In files: stats.py:101; */
"Penniless" = "Penniless";
/* stats.py: Trade rank; In files: stats.py:104; */
/* stats.py: Trade rank; In files: stats.py:102; */
"Mostly Penniless" = "Mostly Penniless";
/* stats.py: Trade rank; In files: stats.py:105; */
/* stats.py: Trade rank; In files: stats.py:103; */
"Peddler" = "Peddler";
/* stats.py: Trade rank; In files: stats.py:106; */
/* stats.py: Trade rank; In files: stats.py:104; */
"Dealer" = "Dealer";
/* stats.py: Trade rank; In files: stats.py:107; */
/* stats.py: Trade rank; In files: stats.py:105; */
"Merchant" = "Merchant";
/* stats.py: Trade rank; In files: stats.py:108; */
/* stats.py: Trade rank; In files: stats.py:106; */
"Broker" = "Broker";
/* stats.py: Trade rank; In files: stats.py:109; */
/* stats.py: Trade rank; In files: stats.py:107; */
"Entrepreneur" = "Entrepreneur";
/* stats.py: Trade rank; In files: stats.py:110; */
/* stats.py: Trade rank; In files: stats.py:108; */
"Tycoon" = "Tycoon";
/* stats.py: Explorer rank; In files: stats.py:113; */
/* stats.py: Explorer rank; In files: stats.py:111; */
"Aimless" = "Aimless";
/* stats.py: Explorer rank; In files: stats.py:114; */
/* stats.py: Explorer rank; In files: stats.py:112; */
"Mostly Aimless" = "Mostly Aimless";
/* stats.py: Explorer rank; In files: stats.py:115; */
/* stats.py: Explorer rank; In files: stats.py:113; */
"Scout" = "Scout";
/* stats.py: Explorer rank; In files: stats.py:116; */
/* stats.py: Explorer rank; In files: stats.py:114; */
"Surveyor" = "Surveyor";
/* stats.py: Explorer rank; In files: stats.py:117; */
/* stats.py: Explorer rank; In files: stats.py:115; */
"Trailblazer" = "Trailblazer";
/* stats.py: Explorer rank; In files: stats.py:118; */
/* stats.py: Explorer rank; In files: stats.py:116; */
"Pathfinder" = "Pathfinder";
/* stats.py: Explorer rank; In files: stats.py:119; */
/* stats.py: Explorer rank; In files: stats.py:117; */
"Ranger" = "Ranger";
/* stats.py: Explorer rank; In files: stats.py:120; */
/* stats.py: Explorer rank; In files: stats.py:118; */
"Pioneer" = "Pioneer";
/* stats.py: CQC rank; In files: stats.py:144; */
/* stats.py: CQC rank; In files: stats.py:142; */
"Helpless" = "Helpless";
/* stats.py: CQC rank; In files: stats.py:145; */
/* stats.py: CQC rank; In files: stats.py:143; */
"Mostly Helpless" = "Mostly Helpless";
/* stats.py: CQC rank; In files: stats.py:146; */
/* stats.py: CQC rank; In files: stats.py:144; */
"Amateur" = "Amateur";
/* stats.py: CQC rank; In files: stats.py:147; */
/* stats.py: CQC rank; In files: stats.py:145; */
"Semi Professional" = "Semi Professional";
/* stats.py: CQC rank; In files: stats.py:148; */
/* stats.py: CQC rank; In files: stats.py:146; */
"Professional" = "Professional";
/* stats.py: CQC rank; In files: stats.py:149; */
/* stats.py: CQC rank; In files: stats.py:147; */
"Champion" = "Champion";
/* stats.py: CQC rank; In files: stats.py:150; */
/* stats.py: CQC rank; In files: stats.py:148; */
"Hero" = "Hero";
/* stats.py: CQC rank; In files: stats.py:151; */
/* stats.py: CQC rank; In files: stats.py:149; */
"Gladiator" = "Gladiator";
/* stats.py: Federation rank; In files: stats.py:157; */
/* stats.py: Federation rank; In files: stats.py:155; */
"Recruit" = "Recruit";
/* stats.py: Federation rank; In files: stats.py:158; */
/* stats.py: Federation rank; In files: stats.py:156; */
"Cadet" = "Cadet";
/* stats.py: Federation rank; In files: stats.py:159; */
/* stats.py: Federation rank; In files: stats.py:157; */
"Midshipman" = "Midshipman";
/* stats.py: Federation rank; In files: stats.py:160; */
/* stats.py: Federation rank; In files: stats.py:158; */
"Petty Officer" = "Petty Officer";
/* stats.py: Federation rank; In files: stats.py:161; */
/* stats.py: Federation rank; In files: stats.py:159; */
"Chief Petty Officer" = "Chief Petty Officer";
/* stats.py: Federation rank; In files: stats.py:162; */
/* stats.py: Federation rank; In files: stats.py:160; */
"Warrant Officer" = "Warrant Officer";
/* stats.py: Federation rank; In files: stats.py:163; */
/* stats.py: Federation rank; In files: stats.py:161; */
"Ensign" = "Ensign";
/* stats.py: Federation rank; In files: stats.py:164; */
/* stats.py: Federation rank; In files: stats.py:162; */
"Lieutenant" = "Lieutenant";
/* stats.py: Federation rank; In files: stats.py:165; */
/* stats.py: Federation rank; In files: stats.py:163; */
"Lieutenant Commander" = "Lieutenant Commander";
/* stats.py: Federation rank; In files: stats.py:166; */
/* stats.py: Federation rank; In files: stats.py:164; */
"Post Commander" = "Post Commander";
/* stats.py: Federation rank; In files: stats.py:167; */
/* stats.py: Federation rank; In files: stats.py:165; */
"Post Captain" = "Post Captain";
/* stats.py: Federation rank; In files: stats.py:168; */
/* stats.py: Federation rank; In files: stats.py:166; */
"Rear Admiral" = "Rear Admiral";
/* stats.py: Federation rank; In files: stats.py:169; */
/* stats.py: Federation rank; In files: stats.py:167; */
"Vice Admiral" = "Vice Admiral";
/* stats.py: Federation rank; In files: stats.py:170; */
/* stats.py: Federation rank; In files: stats.py:168; */
"Admiral" = "Admiral";
/* stats.py: Empire rank; In files: stats.py:176; */
/* stats.py: Empire rank; In files: stats.py:174; */
"Outsider" = "Outsider";
/* stats.py: Empire rank; In files: stats.py:177; */
/* stats.py: Empire rank; In files: stats.py:175; */
"Serf" = "Serf";
/* stats.py: Empire rank; In files: stats.py:179; */
/* stats.py: Empire rank; In files: stats.py:177; */
"Squire" = "Squire";
/* stats.py: Empire rank; In files: stats.py:180; */
/* stats.py: Empire rank; In files: stats.py:178; */
"Knight" = "Knight";
/* stats.py: Empire rank; In files: stats.py:181; */
/* stats.py: Empire rank; In files: stats.py:179; */
"Lord" = "Lord";
/* stats.py: Empire rank; In files: stats.py:182; */
/* stats.py: Empire rank; In files: stats.py:180; */
"Baron" = "Baron";
/* stats.py: Empire rank; In files: stats.py:183; */
/* stats.py: Empire rank; In files: stats.py:181; */
"Viscount" = "Viscount";
/* stats.py: Empire rank; In files: stats.py:184; */
/* stats.py: Empire rank; In files: stats.py:182; */
"Count" = "Count";
/* stats.py: Empire rank; In files: stats.py:185; */
/* stats.py: Empire rank; In files: stats.py:183; */
"Earl" = "Earl";
/* stats.py: Empire rank; In files: stats.py:186; */
/* stats.py: Empire rank; In files: stats.py:184; */
"Marquis" = "Marquis";
/* stats.py: Empire rank; In files: stats.py:187; */
/* stats.py: Empire rank; In files: stats.py:185; */
"Duke" = "Duke";
/* stats.py: Empire rank; In files: stats.py:188; */
/* stats.py: Empire rank; In files: stats.py:186; */
"Prince" = "Prince";
/* stats.py: Empire rank; In files: stats.py:189; */
/* stats.py: Empire rank; In files: stats.py:187; */
"King" = "King";
/* stats.py: Power rank; In files: stats.py:195; */
/* stats.py: Power rank; In files: stats.py:193; */
"Rating 1" = "Rating 1";
/* stats.py: Power rank; In files: stats.py:196; */
/* stats.py: Power rank; In files: stats.py:194; */
"Rating 2" = "Rating 2";
/* stats.py: Power rank; In files: stats.py:197; */
/* stats.py: Power rank; In files: stats.py:195; */
"Rating 3" = "Rating 3";
/* stats.py: Power rank; In files: stats.py:198; */
/* stats.py: Power rank; In files: stats.py:196; */
"Rating 4" = "Rating 4";
/* stats.py: Power rank; In files: stats.py:199; */
/* stats.py: Power rank; In files: stats.py:197; */
"Rating 5" = "Rating 5";
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:413; */
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:409; */
"Value" = "Waarde";
/* stats.py: Status dialog title; In files: stats.py:422; */
/* stats.py: Status dialog title; In files: stats.py:418; */
"Ships" = "Schepen";
+420 -426
View File
File diff suppressed because it is too large. Load diff
+454 -430
View File
File diff suppressed because it is too large. Load diff
+453 -429
View File
File diff suppressed because it is too large. Load diff
+453 -429
View File
File diff suppressed because it is too large. Load diff
+230 -230
View File
@@ -1,434 +1,434 @@
/* Language name */
"!Language" = "Slovenščina";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:212; eddn.py:584; eddn.py:925; load.py:215; load.py:593; load.py:940; eddn.py:252; eddn.py:699; eddn.py:1439; */
"Sending data to EDDN..." = "Pošiljam podatke na EDDN";
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:257; eddn.py:864; eddn.py:898; eddn.py:937; load.py:260; load.py:878; load.py:913; load.py:952; eddn.py:316; eddn.py:1375; eddn.py:1410; eddn.py:1451; */
"Error: Can't connect to EDDN" = "Napaka: povezava z EDDN ni mogoča";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:663; load.py:672; eddn.py:1122; */
"Send station data to the Elite Dangerous Data Network" = "Pošlji podatke postaje na EDDN";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:206; load.py:206; edsm.py:247; */
"Elite Dangerous Star Map credentials" = "EDStarMap uporabniški podatki";
/* edsm.py: Game Commander name label in EDSM settings; theme.py: Label for commander name in main window; EDMarketConnector.py: Label for commander name in main window; stats.py: Cmdr stats; In files: edsm.py:216; load.py:216; edsm.py:258; theme.py:227; EDMarketConnector.py:822; stats.py:59; */
"Cmdr" = "Cmdr";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:223; load.py:223; edsm.py:266; */
"Commander Name" = "Commander ime";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:230; inara.py:233; load.py:230; load.py:233; edsm.py:274; inara.py:243; */
"API Key" = "API ključ";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:256; load.py:256; edsm.py:301; prefs.py:520; prefs.py:1174; prefs.py:1207; stats.py:156; stats.py:175; stats.py:194; stats.py:211; */
"None" = "Brez";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:622; edsm.py:724; load.py:627; load.py:729; edsm.py:747; edsm.py:875; */
"Error: EDSM {MSG}" = "Napaka: EDSM {MSG}";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:658; edsm.py:720; load.py:663; load.py:725; edsm.py:784; edsm.py:870; */
"Error: Can't connect to EDSM" = "Napaka: Povezava z EDSM ni mogoča";
/* ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; In files: ttkHyperlinkLabel.py:42; EDMarketConnector.py:866; */
"Copy" = "Kopiraj";
/* EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:529; EDMarketConnector.py:828; EDMarketConnector.py:1520; */
"Update" = "Osveži";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:612; prefs.py:867; */
"Always on top" = "Vedno na vrhu";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:824; EDMarketConnector.py:1279; */
"Role" = "Vloga";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:824; EDMarketConnector.py:1289; EDMarketConnector.py:1312; stats.py:409; */
"Ship" = "Ladja";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:826; prefs.py:607; stats.py:411; */
"System" = "Sistem";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:827; prefs.py:625; prefs.py:762; stats.py:412; */
"Station" = "Postaja";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:830; EDMarketConnector.py:845; EDMarketConnector.py:848; EDMarketConnector.py:2019; */
"File" = "Datoteka";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:831; EDMarketConnector.py:846; EDMarketConnector.py:849; */
"Edit" = "Uredi";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:832; */
"View" = "Pogled";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:833; */
"Window" = "Okno";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:834; EDMarketConnector.py:847; EDMarketConnector.py:850; */
"Help" = "Pomoč";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:837; EDMarketConnector.py:863; EDMarketConnector.py:1566; */
"About {APP}" = "O {APP}";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:839; EDMarketConnector.py:862; */
"Check for Updates..." = "Preveri posodobitve...";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:840; EDMarketConnector.py:854; */
"Save Raw Data..." = "Shrani Izvirne Podatke...";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:841; EDMarketConnector.py:853; stats.py:406; */
"Status" = "Status";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:842; EDMarketConnector.py:860; */
"Privacy Policy" = "Pravilnik o zasebnosti";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:843; EDMarketConnector.py:861; EDMarketConnector.py:1600; */
"Release Notes" = "Opombe ob izdaji";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:855; EDMarketConnector.py:2020; prefs.py:255; */
"Settings" = "Nastavitve";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:856; */
"Exit" = "Izhod";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:859; */
"Documentation" = "Dokumentacija";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:872; */
"Logging in..." = "Prijava v teku... ";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:888; EDMarketConnector.py:1431; */
"Authentication successful" = "Preverjanje pristnosti je bilo uspešno";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:919; */
"You're not docked at a station!" = "Nahajš se v postaji!";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:927; */
"Station doesn't have anything!" = "Postaja nima ničesar";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:932; */
"Station doesn't have a market!" = "Postaja nima trga";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1006; */
"Fetching data..." = "Nalagam podatke...";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1051; stats.py:335; */
"Who are you?!" = "Kdo si";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1057; stats.py:345; */
"Where are you?!" = "Kje se nahajaš";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1064; stats.py:353; */
"What are you flying?!" = "S čim letiš";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1226; */
"Last updated at %H:%M:%S" = "Zadnja osvežitev podatkov %H:%M:%S ";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1514; */
"cooldown {SS}s" = "Posodobitev v {SS}s";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1626; prefs.py:306; */
"OK" = "Potrdi";
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:263; */
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:271; */
"Error: Invalid Credentials" = "Napaka: napačno uporabniški ime ali geslo";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:194; prefs.py:468; prefs.py:702; prefs.py:735; */
/* EDMarketConnector.py: Main UI Update button; EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:601; EDMarketConnector.py:919; EDMarketConnector.py:1748; */
"Update" = "Osveži";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:710; prefs.py:875; */
"Always on top" = "Vedno na vrhu";
/* EDMarketConnector.py: Label for commander name in main window; edsm.py: Game Commander name label in EDSM settings; stats.py: Cmdr stats; theme.py: Label for commander name in main window; In files: EDMarketConnector.py:913; edsm.py:332; stats.py:57; theme.py:290; */
"Cmdr" = "Cmdr";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:915; EDMarketConnector.py:1487; */
"Role" = "Vloga";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:915; EDMarketConnector.py:1497; EDMarketConnector.py:1520; stats.py:405; */
"Ship" = "Ladja";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:917; prefs.py:614; stats.py:407; */
"System" = "Sistem";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:918; prefs.py:632; prefs.py:770; stats.py:408; */
"Station" = "Postaja";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:921; EDMarketConnector.py:939; EDMarketConnector.py:942; EDMarketConnector.py:2264; */
"File" = "Datoteka";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:922; EDMarketConnector.py:940; EDMarketConnector.py:943; */
"Edit" = "Uredi";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:923; */
"View" = "Pogled";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:924; */
"Window" = "Okno";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:925; EDMarketConnector.py:941; EDMarketConnector.py:944; */
"Help" = "Pomoč";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:928; EDMarketConnector.py:959; EDMarketConnector.py:1804; */
"About {APP}" = "O {APP}";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:930; EDMarketConnector.py:958; */
"Check for Updates..." = "Preveri posodobitve...";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:931; EDMarketConnector.py:948; */
"Save Raw Data..." = "Shrani Izvirne Podatke...";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:932; EDMarketConnector.py:947; stats.py:402; */
"Status" = "Status";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:933; EDMarketConnector.py:953; */
"Documentation" = "Dokumentacija";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:936; EDMarketConnector.py:956; */
"Privacy Policy" = "Pravilnik o zasebnosti";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:937; EDMarketConnector.py:957; EDMarketConnector.py:1838; */
"Release Notes" = "Opombe ob izdaji";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:949; EDMarketConnector.py:2265; prefs.py:241; */
"Settings" = "Nastavitve";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:950; */
"Exit" = "Izhod";
/* EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; In files: EDMarketConnector.py:962; ttkHyperlinkLabel.py:53; */
"Copy" = "Kopiraj";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:978; */
"Logging in..." = "Prijava v teku... ";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:994; EDMarketConnector.py:1657; */
"Authentication successful" = "Preverjanje pristnosti je bilo uspešno";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:1025; */
"You're not docked at a station!" = "Nahajš se v postaji!";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:1033; */
"Station doesn't have anything!" = "Postaja nima ničesar";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:1038; */
"Station doesn't have a market!" = "Postaja nima trga";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1128; EDMarketConnector.py:1179; */
"Fetching data..." = "Nalagam podatke...";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1246; stats.py:333; */
"Who are you?!" = "Kdo si";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1252; stats.py:341; */
"Where are you?!" = "Kje se nahajaš";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1259; stats.py:349; */
"What are you flying?!" = "S čim letiš";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1434; */
"Last updated at %H:%M:%S" = "Zadnja osvežitev podatkov %H:%M:%S ";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1742; */
"cooldown {SS}s" = "Posodobitev v {SS}s";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1864; prefs.py:292; */
"OK" = "Potrdi";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:193; prefs.py:455; prefs.py:709; prefs.py:742; */
"Default" = "Privzeta";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:251; */
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:458; eddn.py:2413; eddn.py:2451; eddn.py:2519; */
"Error: Can't connect to EDDN" = "Napaka: povezava z EDDN ni mogoča";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:2041; */
"Send station data to the Elite Dangerous Data Network" = "Pošlji podatke postaje na EDDN";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:2507; */
"Sending data to EDDN..." = "Pošiljam podatke na EDDN";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:319; */
"Elite Dangerous Star Map credentials" = "EDStarMap uporabniški podatki";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:341; */
"Commander Name" = "Commander ime";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:350; inara.py:278; */
"API Key" = "API ključ";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:394; prefs.py:527; prefs.py:1157; prefs.py:1190; stats.py:154; stats.py:173; stats.py:192; stats.py:209; */
"None" = "Brez";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:916; edsm.py:1048; */
"Error: EDSM {MSG}" = "Napaka: EDSM {MSG}";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:953; edsm.py:1043; */
"Error: Can't connect to EDSM" = "Napaka: Povezava z EDSM ni mogoča";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:237; */
"Preferences" = "Nastavitve";
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:348; */
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:335; */
"Please choose what data to save" = "Izberite podatke, ki jih želite shraniti";
/* prefs.py: Settings > Output option; In files: prefs.py:354; */
/* prefs.py: Settings > Output option; In files: prefs.py:341; */
"Market data in CSV format file" = "Podatki trga v CSV formatu";
/* prefs.py: Settings > Output option; In files: prefs.py:363; */
/* prefs.py: Settings > Output option; In files: prefs.py:350; */
"Market data in Trade Dangerous format file" = "Podatki trga v Trade Dangerous formatu";
/* prefs.py: Settings > Output option; In files: prefs.py:373; */
/* prefs.py: Settings > Output option; In files: prefs.py:360; */
"Ship loadout" = "Oprema ladje";
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:392; prefs.py:411; */
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:379; prefs.py:398; */
"File location" = "Lokacija datoteke";
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:400; prefs.py:450; */
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:387; prefs.py:437; */
"Change..." = "Spremeni...";
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:403; prefs.py:453; */
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:390; prefs.py:440; */
"Browse..." = "Prebrskaj...";
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:418; */
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:405; */
"Output" = "Izpis";
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:484; */
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:490; */
"Keyboard shortcut" = "Bližnjica";
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:486; */
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:492; */
"Hotkey" = "Bližnjica";
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:495; */
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:501; */
"Re-start {APP} to use shortcuts" = "Ponovno poženi {APP} za uporabo bližnjic";
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:504; */
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:510; */
"{APP} needs permission to use shortcuts" = "{APP} potrebuje dovoljenja za uporabo bližnjic";
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:509; */
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:515; */
"Open System Preferences" = "Odpri sistemske nastavitve";
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:531; */
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:538; */
"Only when Elite: Dangerous is the active app" = "Samo ko je Elite:Dangerous aktivna aplikacija ";
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:542; */
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:549; */
"Play sound" = "Sproži zvok";
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:674; */
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:681; */
"Configuration" = "Konfiguracija";
/* prefs.py: Preferences privacy tab title; In files: prefs.py:696; */
/* prefs.py: Preferences privacy tab title; In files: prefs.py:703; */
"Privacy" = "Privatno";
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:720; */
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:727; */
"Language" = "Jezik";
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:730; */
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:737; */
"Theme" = "Tema";
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:741; */
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:749; */
"Dark" = "Temno";
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:748; */
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:756; */
"Transparent" = "Prosojno";
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:885; */
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:893; */
"Appearance" = "Videz";
/* stats.py: Cmdr stats; In files: stats.py:60; */
/* stats.py: Cmdr stats; In files: stats.py:58; */
"Balance" = "Ravnotežje";
/* stats.py: Cmdr stats; In files: stats.py:61; */
/* stats.py: Cmdr stats; In files: stats.py:59; */
"Loan" = "Posojilo";
/* stats.py: Top rank; In files: stats.py:65; */
/* stats.py: Top rank; In files: stats.py:63; */
"Elite" = "Elite";
/* stats.py: Ranking; In files: stats.py:76; */
/* stats.py: Ranking; In files: stats.py:74; */
"Combat" = "Combat";
/* stats.py: Ranking; In files: stats.py:77; */
/* stats.py: Ranking; In files: stats.py:75; */
"Trade" = "Trgovanje";
/* stats.py: Ranking; In files: stats.py:78; */
/* stats.py: Ranking; In files: stats.py:76; */
"Explorer" = "Explorer";
/* stats.py: Ranking; In files: stats.py:81; */
/* stats.py: Ranking; In files: stats.py:79; */
"CQC" = "CQC";
/* stats.py: Ranking; In files: stats.py:82; */
/* stats.py: Ranking; In files: stats.py:80; */
"Federation" = "Federacija";
/* stats.py: Ranking; In files: stats.py:83; */
/* stats.py: Ranking; In files: stats.py:81; */
"Empire" = "Cesarstvo";
/* stats.py: Ranking; In files: stats.py:84; */
/* stats.py: Ranking; In files: stats.py:82; */
"Powerplay" = "Igra moči";
/* stats.py: Combat rank; In files: stats.py:93; */
/* stats.py: Combat rank; In files: stats.py:91; */
"Harmless" = "Harmless";
/* stats.py: Combat rank; In files: stats.py:94; */
/* stats.py: Combat rank; In files: stats.py:92; */
"Mostly Harmless" = "Mostly Harmless";
/* stats.py: Combat rank; In files: stats.py:95; */
/* stats.py: Combat rank; In files: stats.py:93; */
"Novice" = "Novice";
/* stats.py: Combat rank; In files: stats.py:96; */
/* stats.py: Combat rank; In files: stats.py:94; */
"Competent" = "Competent";
/* stats.py: Combat rank; In files: stats.py:97; */
/* stats.py: Combat rank; In files: stats.py:95; */
"Expert" = "Expert";
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:98; stats.py:178; */
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:96; stats.py:176; */
"Master" = "Master";
/* stats.py: Combat rank; In files: stats.py:99; */
/* stats.py: Combat rank; In files: stats.py:97; */
"Dangerous" = "Dangerous";
/* stats.py: Combat rank; In files: stats.py:100; */
/* stats.py: Combat rank; In files: stats.py:98; */
"Deadly" = "Deadly";
/* stats.py: Trade rank; In files: stats.py:103; */
/* stats.py: Trade rank; In files: stats.py:101; */
"Penniless" = "Peniless";
/* stats.py: Trade rank; In files: stats.py:104; */
/* stats.py: Trade rank; In files: stats.py:102; */
"Mostly Penniless" = "Mostly peniless";
/* stats.py: Trade rank; In files: stats.py:105; */
/* stats.py: Trade rank; In files: stats.py:103; */
"Peddler" = "Peddler";
/* stats.py: Trade rank; In files: stats.py:106; */
/* stats.py: Trade rank; In files: stats.py:104; */
"Dealer" = "Dealer";
/* stats.py: Trade rank; In files: stats.py:107; */
/* stats.py: Trade rank; In files: stats.py:105; */
"Merchant" = "Merchant";
/* stats.py: Trade rank; In files: stats.py:108; */
/* stats.py: Trade rank; In files: stats.py:106; */
"Broker" = "Broker";
/* stats.py: Trade rank; In files: stats.py:109; */
/* stats.py: Trade rank; In files: stats.py:107; */
"Entrepreneur" = "Enterpreneur";
/* stats.py: Trade rank; In files: stats.py:110; */
/* stats.py: Trade rank; In files: stats.py:108; */
"Tycoon" = "Tycoon";
/* stats.py: Explorer rank; In files: stats.py:113; */
/* stats.py: Explorer rank; In files: stats.py:111; */
"Aimless" = "Aimless";
/* stats.py: Explorer rank; In files: stats.py:114; */
/* stats.py: Explorer rank; In files: stats.py:112; */
"Mostly Aimless" = "Mostly Aimless";
/* stats.py: Explorer rank; In files: stats.py:115; */
/* stats.py: Explorer rank; In files: stats.py:113; */
"Scout" = "Scout";
/* stats.py: Explorer rank; In files: stats.py:116; */
/* stats.py: Explorer rank; In files: stats.py:114; */
"Surveyor" = "Surveyor";
/* stats.py: Explorer rank; In files: stats.py:117; */
/* stats.py: Explorer rank; In files: stats.py:115; */
"Trailblazer" = "Trailblazer";
/* stats.py: Explorer rank; In files: stats.py:118; */
/* stats.py: Explorer rank; In files: stats.py:116; */
"Pathfinder" = "Pathfinder";
/* stats.py: Explorer rank; In files: stats.py:119; */
/* stats.py: Explorer rank; In files: stats.py:117; */
"Ranger" = "Ranger";
/* stats.py: Explorer rank; In files: stats.py:120; */
/* stats.py: Explorer rank; In files: stats.py:118; */
"Pioneer" = "Pioneer";
/* stats.py: CQC rank; In files: stats.py:144; */
/* stats.py: CQC rank; In files: stats.py:142; */
"Helpless" = "Helpless";
/* stats.py: CQC rank; In files: stats.py:145; */
/* stats.py: CQC rank; In files: stats.py:143; */
"Mostly Helpless" = "Mostly Helpless";
/* stats.py: CQC rank; In files: stats.py:146; */
/* stats.py: CQC rank; In files: stats.py:144; */
"Amateur" = "Amateur";
/* stats.py: CQC rank; In files: stats.py:147; */
/* stats.py: CQC rank; In files: stats.py:145; */
"Semi Professional" = "Semi Professional";
/* stats.py: CQC rank; In files: stats.py:148; */
/* stats.py: CQC rank; In files: stats.py:146; */
"Professional" = "Professional";
/* stats.py: CQC rank; In files: stats.py:149; */
/* stats.py: CQC rank; In files: stats.py:147; */
"Champion" = "Champion";
/* stats.py: CQC rank; In files: stats.py:150; */
/* stats.py: CQC rank; In files: stats.py:148; */
"Hero" = "Hero";
/* stats.py: CQC rank; In files: stats.py:151; */
/* stats.py: CQC rank; In files: stats.py:149; */
"Gladiator" = "Gladiator";
/* stats.py: Federation rank; In files: stats.py:157; */
/* stats.py: Federation rank; In files: stats.py:155; */
"Recruit" = "Recruit";
/* stats.py: Federation rank; In files: stats.py:158; */
/* stats.py: Federation rank; In files: stats.py:156; */
"Cadet" = "Cadet";
/* stats.py: Federation rank; In files: stats.py:159; */
/* stats.py: Federation rank; In files: stats.py:157; */
"Midshipman" = "Midshipman";
/* stats.py: Federation rank; In files: stats.py:160; */
/* stats.py: Federation rank; In files: stats.py:158; */
"Petty Officer" = "Petty Officer";
/* stats.py: Federation rank; In files: stats.py:161; */
/* stats.py: Federation rank; In files: stats.py:159; */
"Chief Petty Officer" = "Chief Petty Officer";
/* stats.py: Federation rank; In files: stats.py:162; */
/* stats.py: Federation rank; In files: stats.py:160; */
"Warrant Officer" = "Warrant Officer";
/* stats.py: Federation rank; In files: stats.py:163; */
/* stats.py: Federation rank; In files: stats.py:161; */
"Ensign" = "Ensign";
/* stats.py: Federation rank; In files: stats.py:164; */
/* stats.py: Federation rank; In files: stats.py:162; */
"Lieutenant" = "LIeutenant";
/* stats.py: Federation rank; In files: stats.py:165; */
/* stats.py: Federation rank; In files: stats.py:163; */
"Lieutenant Commander" = "Lieutenant Commander";
/* stats.py: Federation rank; In files: stats.py:166; */
/* stats.py: Federation rank; In files: stats.py:164; */
"Post Commander" = "Post Commander";
/* stats.py: Federation rank; In files: stats.py:167; */
/* stats.py: Federation rank; In files: stats.py:165; */
"Post Captain" = "Post Captain";
/* stats.py: Federation rank; In files: stats.py:168; */
/* stats.py: Federation rank; In files: stats.py:166; */
"Rear Admiral" = "Read Admiral";
/* stats.py: Federation rank; In files: stats.py:169; */
/* stats.py: Federation rank; In files: stats.py:167; */
"Vice Admiral" = "Vice Admiral";
/* stats.py: Federation rank; In files: stats.py:170; */
/* stats.py: Federation rank; In files: stats.py:168; */
"Admiral" = "Admiral";
/* stats.py: Empire rank; In files: stats.py:176; */
/* stats.py: Empire rank; In files: stats.py:174; */
"Outsider" = "Outsider";
/* stats.py: Empire rank; In files: stats.py:177; */
/* stats.py: Empire rank; In files: stats.py:175; */
"Serf" = "Serif";
/* stats.py: Empire rank; In files: stats.py:179; */
/* stats.py: Empire rank; In files: stats.py:177; */
"Squire" = "Squire";
/* stats.py: Empire rank; In files: stats.py:180; */
/* stats.py: Empire rank; In files: stats.py:178; */
"Knight" = "Knight";
/* stats.py: Empire rank; In files: stats.py:181; */
/* stats.py: Empire rank; In files: stats.py:179; */
"Lord" = "Lord";
/* stats.py: Empire rank; In files: stats.py:182; */
/* stats.py: Empire rank; In files: stats.py:180; */
"Baron" = "Baron";
/* stats.py: Empire rank; In files: stats.py:183; */
/* stats.py: Empire rank; In files: stats.py:181; */
"Viscount" = "Viscount";
/* stats.py: Empire rank; In files: stats.py:184; */
/* stats.py: Empire rank; In files: stats.py:182; */
"Count" = "Count";
/* stats.py: Empire rank; In files: stats.py:185; */
/* stats.py: Empire rank; In files: stats.py:183; */
"Earl" = "Earl";
/* stats.py: Empire rank; In files: stats.py:186; */
/* stats.py: Empire rank; In files: stats.py:184; */
"Marquis" = "Marquis";
/* stats.py: Empire rank; In files: stats.py:187; */
/* stats.py: Empire rank; In files: stats.py:185; */
"Duke" = "Duke";
/* stats.py: Empire rank; In files: stats.py:188; */
/* stats.py: Empire rank; In files: stats.py:186; */
"Prince" = "Prince";
/* stats.py: Empire rank; In files: stats.py:189; */
/* stats.py: Empire rank; In files: stats.py:187; */
"King" = "King";
/* stats.py: Power rank; In files: stats.py:195; */
/* stats.py: Power rank; In files: stats.py:193; */
"Rating 1" = "Ocena 1";
/* stats.py: Power rank; In files: stats.py:196; */
/* stats.py: Power rank; In files: stats.py:194; */
"Rating 2" = "Ocena 2";
/* stats.py: Power rank; In files: stats.py:197; */
/* stats.py: Power rank; In files: stats.py:195; */
"Rating 3" = "Ocena 3";
/* stats.py: Power rank; In files: stats.py:198; */
/* stats.py: Power rank; In files: stats.py:196; */
"Rating 4" = "Ocena 4";
/* stats.py: Power rank; In files: stats.py:199; */
/* stats.py: Power rank; In files: stats.py:197; */
"Rating 5" = "Ocena 5";
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:413; */
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:409; */
"Value" = "Vrednost";
/* stats.py: Status dialog title; In files: stats.py:422; */
/* stats.py: Status dialog title; In files: stats.py:418; */
"Ships" = "Ladje";
+423 -429
View File
File diff suppressed because it is too large. Load diff
+453 -429
View File
File diff suppressed because it is too large. Load diff
+280 -280
View File
@@ -1,512 +1,512 @@
/* Language name */
"!Language" = "Svenska";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:212; eddn.py:584; eddn.py:925; load.py:215; load.py:593; load.py:940; eddn.py:252; eddn.py:699; eddn.py:1439; */
"Sending data to EDDN..." = "Skickar data till EDDN...";
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:257; eddn.py:864; eddn.py:898; eddn.py:937; load.py:260; load.py:878; load.py:913; load.py:952; eddn.py:316; eddn.py:1375; eddn.py:1410; eddn.py:1451; */
"Error: Can't connect to EDDN" = "Fel: Kan inte ansluta till EDDN";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:663; load.py:672; eddn.py:1122; */
"Send station data to the Elite Dangerous Data Network" = "Skicka stationsdata till Elite Dangerous Data Network";
/* eddn.py: Enable EDDN support for system and other scan data checkbox label; In files: eddn.py:673; load.py:682; eddn.py:1133; */
"Send system and scan data to the Elite Dangerous Data Network" = "Skicka system och scan-data till Elite Dangerous Data Network";
/* eddn.py: EDDN delay sending until docked option is on, this message notes that a send was skipped due to this; In files: eddn.py:683; load.py:692; eddn.py:1144; */
"Delay sending until docked" = "Skicka bara uppdateringar till EDDN när du är dockad";
/* edsm.py: Settings>EDSM - Label on checkbox for 'send data'; In files: edsm.py:197; load.py:197; edsm.py:237; */
"Send flight log and Cmdr status to EDSM" = "Skicka flight log-data och Cmdr-status till EDSM";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:206; load.py:206; edsm.py:247; */
"Elite Dangerous Star Map credentials" = "Elite Dangerous Star Map användaruppgifter";
/* edsm.py: Game Commander name label in EDSM settings; theme.py: Label for commander name in main window; EDMarketConnector.py: Label for commander name in main window; stats.py: Cmdr stats; In files: edsm.py:216; load.py:216; edsm.py:258; theme.py:227; EDMarketConnector.py:822; stats.py:59; */
"Cmdr" = "Cmdr";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:223; load.py:223; edsm.py:266; */
"Commander Name" = "CMDR namn";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:230; inara.py:233; load.py:230; load.py:233; edsm.py:274; inara.py:243; */
"API Key" = "API Nyckel";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:256; load.py:256; edsm.py:301; prefs.py:520; prefs.py:1174; prefs.py:1207; stats.py:156; stats.py:175; stats.py:194; stats.py:211; */
"None" = "Ingen";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:622; edsm.py:724; load.py:627; load.py:729; edsm.py:747; edsm.py:875; */
"Error: EDSM {MSG}" = "Fel: EDSM {MSG}";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:658; edsm.py:720; load.py:663; load.py:725; edsm.py:784; edsm.py:870; */
"Error: Can't connect to EDSM" = "Fel: Kan inte ansluta till EDSM";
/* inara.py: Checkbox to enable INARA API Usage; In files: inara.py:215; load.py:215; inara.py:222; */
"Send flight log and Cmdr status to Inara" = "Skicka flight log-data och Cmdr-status till Inara";
/* inara.py: Text for INARA API keys link ( goes to https://inara.cz/settings-api ); In files: inara.py:225; load.py:225; inara.py:234; */
"Inara credentials" = "Inara autentiseringsuppgifter";
/* inara.py: INARA API returned some kind of error (error message will be contained in {MSG}); In files: inara.py:1316; inara.py:1328; load.py:1319; load.py:1331; inara.py:1587; inara.py:1600; */
"Error: Inara {MSG}" = "Fel: Inara {MSG}";
/* ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; In files: ttkHyperlinkLabel.py:42; EDMarketConnector.py:866; */
"Copy" = "Kopiera";
/* EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:529; EDMarketConnector.py:828; EDMarketConnector.py:1520; */
"Update" = "Uppdatera";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:612; prefs.py:867; */
"Always on top" = "Alltid överst";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:824; EDMarketConnector.py:1279; */
"Role" = "Roll";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:824; EDMarketConnector.py:1289; EDMarketConnector.py:1312; stats.py:409; */
"Ship" = "Skepp";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:826; prefs.py:607; stats.py:411; */
"System" = "System";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:827; prefs.py:625; prefs.py:762; stats.py:412; */
"Station" = "Station";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:830; EDMarketConnector.py:845; EDMarketConnector.py:848; EDMarketConnector.py:2019; */
"File" = "Fil";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:831; EDMarketConnector.py:846; EDMarketConnector.py:849; */
"Edit" = "Ändra";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:832; */
"View" = "View";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:833; */
"Window" = "Fönster";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:834; EDMarketConnector.py:847; EDMarketConnector.py:850; */
"Help" = "Hjälp";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:837; EDMarketConnector.py:863; EDMarketConnector.py:1566; */
"About {APP}" = "Om {APP}";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:839; EDMarketConnector.py:862; */
"Check for Updates..." = "Sök efter uppdateringar...";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:840; EDMarketConnector.py:854; */
"Save Raw Data..." = "Lagra rådata...";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:841; EDMarketConnector.py:853; stats.py:406; */
"Status" = "Status";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:842; EDMarketConnector.py:860; */
"Privacy Policy" = "Integritetspolicy";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:843; EDMarketConnector.py:861; EDMarketConnector.py:1600; */
"Release Notes" = "Versionsinformation";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:855; EDMarketConnector.py:2020; prefs.py:255; */
"Settings" = "Inställningar";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:856; */
"Exit" = "Avsluta";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:859; */
"Documentation" = "Dokumentation";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:872; */
"Logging in..." = "Loggar in...";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:888; EDMarketConnector.py:1431; */
"Authentication successful" = "Autentisering lyckad";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:919; */
"You're not docked at a station!" = "Du är inte dockad vid en station!";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:927; */
"Station doesn't have anything!" = "Stationen är tom!";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:932; */
"Station doesn't have a market!" = "Stationen har ingen marknad!";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1006; */
"Fetching data..." = "Hämtar data...";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1051; stats.py:335; */
"Who are you?!" = "Vem är du? (felmeddelande)";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1057; stats.py:345; */
"Where are you?!" = "Vart är du? (felmeddelande)";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1064; stats.py:353; */
"What are you flying?!" = "Okänt skepp? (felmeddelande)";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1226; */
"Last updated at %H:%M:%S" = "Senaste uppdatering: %H:%M:%S";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1254; */
"Fighter" = "Fighter";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1255; */
"Gunner" = "Gunner";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1256; */
"Helm" = "Helm";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1514; */
"cooldown {SS}s" = "Vänta {SS}s";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1626; prefs.py:306; */
"OK" = "OK";
/* EDMarketConnector.py: Popup-text about 'active' plugins without Python 3.x support; In files: EDMarketConnector.py:2008:2014; */
"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." = "En eller flera aktiverade plugins har inte stöd för Python 3.x. Var vänlig kontrollera listan med plugins under fliken '{PLUGINS}' under '{FILE}' > '{SETTINGS}'. Kontrollera om det finns en nyare version tillgänglig eller informera utvecklaren.\n\nDu kan också avaktivera din plugin genom att ändra slutet av namnet på mappen till '{DISABLED}'";
/* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2018; prefs.py:978; */
"Plugins" = "Plugins";
/* EDMarketConnector.py: Popup window title for list of 'enabled' plugins that don't work with Python 3.x; In files: EDMarketConnector.py:2029; */
"EDMC: Plugins Without Python 3.x Support" = "EDMC: Plugins utan stöd för Python 3.x";
/* companion.py: Frontier CAPI data doesn't agree with latest Journal game location; In files: companion.py:237; */
/* companion.py: Frontier CAPI data doesn't agree with latest Journal game location; In files: companion.py:245; */
"Error: Frontier server is lagging" = "Fel: Frontier lag på server";
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:263; */
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:271; */
"Error: Invalid Credentials" = "Fel: ogiltligt användarnamn eller lösenord";
/* companion.py: Frontier CAPI authorisation not for currently game-active commander; In files: companion.py:288; */
/* companion.py: Frontier CAPI authorisation not for currently game-active commander; In files: companion.py:296; */
"Error: Wrong Cmdr" = "Fel: Fel användare. Har du flera konton eller en gammal API-nyckel från ett nollställt konto?";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:194; prefs.py:468; prefs.py:702; prefs.py:735; */
/* EDMarketConnector.py: Main UI Update button; EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:601; EDMarketConnector.py:919; EDMarketConnector.py:1748; */
"Update" = "Uppdatera";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:710; prefs.py:875; */
"Always on top" = "Alltid överst";
/* EDMarketConnector.py: Label for commander name in main window; edsm.py: Game Commander name label in EDSM settings; stats.py: Cmdr stats; theme.py: Label for commander name in main window; In files: EDMarketConnector.py:913; edsm.py:332; stats.py:57; theme.py:290; */
"Cmdr" = "Cmdr";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:915; EDMarketConnector.py:1487; */
"Role" = "Roll";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:915; EDMarketConnector.py:1497; EDMarketConnector.py:1520; stats.py:405; */
"Ship" = "Skepp";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:917; prefs.py:614; stats.py:407; */
"System" = "System";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:918; prefs.py:632; prefs.py:770; stats.py:408; */
"Station" = "Station";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:921; EDMarketConnector.py:939; EDMarketConnector.py:942; EDMarketConnector.py:2264; */
"File" = "Fil";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:922; EDMarketConnector.py:940; EDMarketConnector.py:943; */
"Edit" = "Ändra";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:923; */
"View" = "View";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:924; */
"Window" = "Fönster";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:925; EDMarketConnector.py:941; EDMarketConnector.py:944; */
"Help" = "Hjälp";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:928; EDMarketConnector.py:959; EDMarketConnector.py:1804; */
"About {APP}" = "Om {APP}";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:930; EDMarketConnector.py:958; */
"Check for Updates..." = "Sök efter uppdateringar...";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:931; EDMarketConnector.py:948; */
"Save Raw Data..." = "Lagra rådata...";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:932; EDMarketConnector.py:947; stats.py:402; */
"Status" = "Status";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:933; EDMarketConnector.py:953; */
"Documentation" = "Dokumentation";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:936; EDMarketConnector.py:956; */
"Privacy Policy" = "Integritetspolicy";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:937; EDMarketConnector.py:957; EDMarketConnector.py:1838; */
"Release Notes" = "Versionsinformation";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:949; EDMarketConnector.py:2265; prefs.py:241; */
"Settings" = "Inställningar";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:950; */
"Exit" = "Avsluta";
/* EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; In files: EDMarketConnector.py:962; ttkHyperlinkLabel.py:53; */
"Copy" = "Kopiera";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:978; */
"Logging in..." = "Loggar in...";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:994; EDMarketConnector.py:1657; */
"Authentication successful" = "Autentisering lyckad";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:1025; */
"You're not docked at a station!" = "Du är inte dockad vid en station!";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:1033; */
"Station doesn't have anything!" = "Stationen är tom!";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:1038; */
"Station doesn't have a market!" = "Stationen har ingen marknad!";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1128; EDMarketConnector.py:1179; */
"Fetching data..." = "Hämtar data...";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1246; stats.py:333; */
"Who are you?!" = "Vem är du? (felmeddelande)";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1252; stats.py:341; */
"Where are you?!" = "Vart är du? (felmeddelande)";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1259; stats.py:349; */
"What are you flying?!" = "Okänt skepp? (felmeddelande)";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1434; */
"Last updated at %H:%M:%S" = "Senaste uppdatering: %H:%M:%S";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1462; */
"Fighter" = "Fighter";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1463; */
"Gunner" = "Gunner";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1464; */
"Helm" = "Helm";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1742; */
"cooldown {SS}s" = "Vänta {SS}s";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1864; prefs.py:292; */
"OK" = "OK";
/* EDMarketConnector.py: Popup-text about 'active' plugins without Python 3.x support; In files: EDMarketConnector.py:2253:2259; */
"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." = "En eller flera aktiverade plugins har inte stöd för Python 3.x. Var vänlig kontrollera listan med plugins under fliken '{PLUGINS}' under '{FILE}' > '{SETTINGS}'. Kontrollera om det finns en nyare version tillgänglig eller informera utvecklaren.\n\nDu kan också avaktivera din plugin genom att ändra slutet av namnet på mappen till '{DISABLED}'";
/* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2263; prefs.py:986; */
"Plugins" = "Plugins";
/* EDMarketConnector.py: Popup window title for list of 'enabled' plugins that don't work with Python 3.x; In files: EDMarketConnector.py:2274; */
"EDMC: Plugins Without Python 3.x Support" = "EDMC: Plugins utan stöd för Python 3.x";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:193; prefs.py:455; prefs.py:709; prefs.py:742; */
"Default" = "Standard";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:251; */
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:458; eddn.py:2413; eddn.py:2451; eddn.py:2519; */
"Error: Can't connect to EDDN" = "Fel: Kan inte ansluta till EDDN";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:2041; */
"Send station data to the Elite Dangerous Data Network" = "Skicka stationsdata till Elite Dangerous Data Network";
/* eddn.py: Enable EDDN support for system and other scan data checkbox label; In files: eddn.py:2052; */
"Send system and scan data to the Elite Dangerous Data Network" = "Skicka system och scan-data till Elite Dangerous Data Network";
/* eddn.py: EDDN delay sending until docked option is on, this message notes that a send was skipped due to this; In files: eddn.py:2063; */
"Delay sending until docked" = "Skicka bara uppdateringar till EDDN när du är dockad";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:2507; */
"Sending data to EDDN..." = "Skickar data till EDDN...";
/* edsm.py: Settings>EDSM - Label on checkbox for 'send data'; In files: edsm.py:308; */
"Send flight log and Cmdr status to EDSM" = "Skicka flight log-data och Cmdr-status till EDSM";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:319; */
"Elite Dangerous Star Map credentials" = "Elite Dangerous Star Map användaruppgifter";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:341; */
"Commander Name" = "CMDR namn";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:350; inara.py:278; */
"API Key" = "API Nyckel";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:394; prefs.py:527; prefs.py:1157; prefs.py:1190; stats.py:154; stats.py:173; stats.py:192; stats.py:209; */
"None" = "Ingen";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:916; edsm.py:1048; */
"Error: EDSM {MSG}" = "Fel: EDSM {MSG}";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:953; edsm.py:1043; */
"Error: Can't connect to EDSM" = "Fel: Kan inte ansluta till EDSM";
/* inara.py: Checkbox to enable INARA API Usage; In files: inara.py:257; */
"Send flight log and Cmdr status to Inara" = "Skicka flight log-data och Cmdr-status till Inara";
/* inara.py: Text for INARA API keys link ( goes to https://inara.cz/settings-api ); In files: inara.py:269; */
"Inara credentials" = "Inara autentiseringsuppgifter";
/* inara.py: INARA API returned some kind of error (error message will be contained in {MSG}); In files: inara.py:1650; inara.py:1663; */
"Error: Inara {MSG}" = "Fel: Inara {MSG}";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:237; */
"Preferences" = "Inställningar";
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:348; */
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:335; */
"Please choose what data to save" = "Välj vilket data som skall lagras";
/* prefs.py: Settings > Output option; In files: prefs.py:354; */
/* prefs.py: Settings > Output option; In files: prefs.py:341; */
"Market data in CSV format file" = "Marknadsdata i CSV-formaterad fil";
/* prefs.py: Settings > Output option; In files: prefs.py:363; */
/* prefs.py: Settings > Output option; In files: prefs.py:350; */
"Market data in Trade Dangerous format file" = "Marknadsdata i Trade Dangerous-formaterad fil";
/* prefs.py: Settings > Output option; In files: prefs.py:373; */
/* prefs.py: Settings > Output option; In files: prefs.py:360; */
"Ship loadout" = "Översikt av skepp";
/* prefs.py: Settings > Output option; In files: prefs.py:383; */
/* prefs.py: Settings > Output option; In files: prefs.py:370; */
"Automatically update on docking" = "Automatisk uppdatering vid dockning";
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:392; prefs.py:411; */
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:379; prefs.py:398; */
"File location" = "Filsökväg";
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:400; prefs.py:450; */
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:387; prefs.py:437; */
"Change..." = "Ändra...";
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:403; prefs.py:453; */
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:390; prefs.py:440; */
"Browse..." = "Bläddra...";
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:418; */
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:405; */
"Output" = "Exportdata";
/* prefs.py: Settings > Configuration - Label for Journal files location; In files: prefs.py:444; prefs.py:459; */
/* prefs.py: Settings > Configuration - Label for Journal files location; In files: prefs.py:431; prefs.py:446; */
"E:D journal file location" = "E:D journal-fil sökväg";
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:484; */
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:490; */
"Keyboard shortcut" = "Genväg, tangentbord";
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:486; */
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:492; */
"Hotkey" = "Snabbkommando";
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:495; */
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:501; */
"Re-start {APP} to use shortcuts" = "Starta om {APP} för att använda genvägar";
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:504; */
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:510; */
"{APP} needs permission to use shortcuts" = "{APP} behöver rättigheter för att använda genvägar";
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:509; */
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:515; */
"Open System Preferences" = "Öppna systeminställningar";
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:531; */
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:538; */
"Only when Elite: Dangerous is the active app" = "Bara när: Elite Dangerous är det aktiva programmet";
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:542; */
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:549; */
"Play sound" = "Spela ljud";
/* prefs.py: Configuration - disable checks for app updates when in-game; In files: prefs.py:557; */
/* prefs.py: Configuration - disable checks for app updates when in-game; In files: prefs.py:564; */
"Disable Automatic Application Updates Check when in-game" = "Leta inte efter programuppdateringar när spelet körs";
/* prefs.py: Label for preferred shipyard, system and station 'providers'; In files: prefs.py:570; */
/* prefs.py: Label for preferred shipyard, system and station 'providers'; In files: prefs.py:577; */
"Preferred websites" = "Föredragna webbsidor";
/* prefs.py: Label for Shipyard provider selection; In files: prefs.py:581; */
/* prefs.py: Label for Shipyard provider selection; In files: prefs.py:588; */
"Shipyard" = "Översikt av skepp";
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:674; */
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:681; */
"Configuration" = "Konfiguration";
/* prefs.py: Label for Settings > Appeareance > selection of 'normal' text colour; In files: prefs.py:709; */
/* prefs.py: Label for Settings > Appeareance > selection of 'normal' text colour; In files: prefs.py:716; */
"Normal text" = "Normal text";
/* prefs.py: Label for Settings > Appeareance > selection of 'highlightes' text colour; In files: prefs.py:711; */
/* prefs.py: Label for Settings > Appeareance > selection of 'highlightes' text colour; In files: prefs.py:718; */
"Highlighted text" = "Markerad text";
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:720; */
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:727; */
"Language" = "Språk";
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:730; */
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:737; */
"Theme" = "Tema";
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:741; */
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:749; */
"Dark" = "Mörk";
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:748; */
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:756; */
"Transparent" = "Transparent";
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:885; */
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:893; */
"Appearance" = "Utseende";
/* prefs.py: Label for location of third-party plugins folder; In files: prefs.py:900; */
/* prefs.py: Label for location of third-party plugins folder; In files: prefs.py:908; */
"Plugins folder" = "Mapp för plugins";
/* prefs.py: Label on button used to open a filesystem folder; In files: prefs.py:907; */
/* prefs.py: Label on button used to open a filesystem folder; In files: prefs.py:915; */
"Open" = "Öppna";
/* prefs.py: Tip/label about how to disable plugins; In files: prefs.py:915; */
/* prefs.py: Tip/label about how to disable plugins; In files: prefs.py:923; */
"Tip: You can disable a plugin by{CR}adding '{EXT}' to its folder name" = "Tips: du kan avaktivera en plugin genom{CR}att lägga till '{EXT}' på slutet av mappens namn";
/* prefs.py: Label on list of enabled plugins; In files: prefs.py:926; */
/* prefs.py: Label on list of enabled plugins; In files: prefs.py:934; */
"Enabled Plugins" = "Aktiverade plugins";
/* prefs.py: Plugins - Label on URL to documentation about migrating plugins from Python 2.7; In files: prefs.py:954; */
/* prefs.py: Plugins - Label on URL to documentation about migrating plugins from Python 2.7; In files: prefs.py:962; */
"Information on migrating plugins" = "Information om migrering av plugins";
/* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:969; */
/* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:977; */
"Disabled Plugins" = "Avaktiverade plugins";
/* stats.py: Cmdr stats; In files: stats.py:60; */
/* stats.py: Cmdr stats; In files: stats.py:58; */
"Balance" = "Balance";
/* stats.py: Cmdr stats; In files: stats.py:61; */
/* stats.py: Cmdr stats; In files: stats.py:59; */
"Loan" = "Lån";
/* stats.py: Top rank; In files: stats.py:65; */
/* stats.py: Top rank; In files: stats.py:63; */
"Elite" = "Elite";
/* stats.py: Ranking; In files: stats.py:76; */
/* stats.py: Ranking; In files: stats.py:74; */
"Combat" = "Combat";
/* stats.py: Ranking; In files: stats.py:77; */
/* stats.py: Ranking; In files: stats.py:75; */
"Trade" = "Trade";
/* stats.py: Ranking; In files: stats.py:78; */
/* stats.py: Ranking; In files: stats.py:76; */
"Explorer" = "Explorer";
/* stats.py: Ranking; In files: stats.py:81; */
/* stats.py: Ranking; In files: stats.py:79; */
"CQC" = "CQC";
/* stats.py: Ranking; In files: stats.py:82; */
/* stats.py: Ranking; In files: stats.py:80; */
"Federation" = "Federation";
/* stats.py: Ranking; In files: stats.py:83; */
/* stats.py: Ranking; In files: stats.py:81; */
"Empire" = "Empire";
/* stats.py: Ranking; In files: stats.py:84; */
/* stats.py: Ranking; In files: stats.py:82; */
"Powerplay" = "Powerplay";
/* stats.py: Combat rank; In files: stats.py:93; */
/* stats.py: Combat rank; In files: stats.py:91; */
"Harmless" = "Harmless";
/* stats.py: Combat rank; In files: stats.py:94; */
/* stats.py: Combat rank; In files: stats.py:92; */
"Mostly Harmless" = "Mostly Harmless";
/* stats.py: Combat rank; In files: stats.py:95; */
/* stats.py: Combat rank; In files: stats.py:93; */
"Novice" = "Novice";
/* stats.py: Combat rank; In files: stats.py:96; */
/* stats.py: Combat rank; In files: stats.py:94; */
"Competent" = "Competent";
/* stats.py: Combat rank; In files: stats.py:97; */
/* stats.py: Combat rank; In files: stats.py:95; */
"Expert" = "Expert";
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:98; stats.py:178; */
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:96; stats.py:176; */
"Master" = "Master";
/* stats.py: Combat rank; In files: stats.py:99; */
/* stats.py: Combat rank; In files: stats.py:97; */
"Dangerous" = "Dangerous";
/* stats.py: Combat rank; In files: stats.py:100; */
/* stats.py: Combat rank; In files: stats.py:98; */
"Deadly" = "Deadly";
/* stats.py: Trade rank; In files: stats.py:103; */
/* stats.py: Trade rank; In files: stats.py:101; */
"Penniless" = "Penniless";
/* stats.py: Trade rank; In files: stats.py:104; */
/* stats.py: Trade rank; In files: stats.py:102; */
"Mostly Penniless" = "Mostly Penniless";
/* stats.py: Trade rank; In files: stats.py:105; */
/* stats.py: Trade rank; In files: stats.py:103; */
"Peddler" = "Peddler";
/* stats.py: Trade rank; In files: stats.py:106; */
/* stats.py: Trade rank; In files: stats.py:104; */
"Dealer" = "Dealer";
/* stats.py: Trade rank; In files: stats.py:107; */
/* stats.py: Trade rank; In files: stats.py:105; */
"Merchant" = "Merchant";
/* stats.py: Trade rank; In files: stats.py:108; */
/* stats.py: Trade rank; In files: stats.py:106; */
"Broker" = "Broker";
/* stats.py: Trade rank; In files: stats.py:109; */
/* stats.py: Trade rank; In files: stats.py:107; */
"Entrepreneur" = "Entrepreneur";
/* stats.py: Trade rank; In files: stats.py:110; */
/* stats.py: Trade rank; In files: stats.py:108; */
"Tycoon" = "Tycoon";
/* stats.py: Explorer rank; In files: stats.py:113; */
/* stats.py: Explorer rank; In files: stats.py:111; */
"Aimless" = "Aimless";
/* stats.py: Explorer rank; In files: stats.py:114; */
/* stats.py: Explorer rank; In files: stats.py:112; */
"Mostly Aimless" = "Mostly Aimless";
/* stats.py: Explorer rank; In files: stats.py:115; */
/* stats.py: Explorer rank; In files: stats.py:113; */
"Scout" = "Scout";
/* stats.py: Explorer rank; In files: stats.py:116; */
/* stats.py: Explorer rank; In files: stats.py:114; */
"Surveyor" = "Surveyor";
/* stats.py: Explorer rank; In files: stats.py:117; */
/* stats.py: Explorer rank; In files: stats.py:115; */
"Trailblazer" = "Trailblazer";
/* stats.py: Explorer rank; In files: stats.py:118; */
/* stats.py: Explorer rank; In files: stats.py:116; */
"Pathfinder" = "Pathfinder";
/* stats.py: Explorer rank; In files: stats.py:119; */
/* stats.py: Explorer rank; In files: stats.py:117; */
"Ranger" = "Ranger";
/* stats.py: Explorer rank; In files: stats.py:120; */
/* stats.py: Explorer rank; In files: stats.py:118; */
"Pioneer" = "Pioneer";
/* stats.py: CQC rank; In files: stats.py:144; */
/* stats.py: CQC rank; In files: stats.py:142; */
"Helpless" = "Helpless";
/* stats.py: CQC rank; In files: stats.py:145; */
/* stats.py: CQC rank; In files: stats.py:143; */
"Mostly Helpless" = "Mostly Helpless";
/* stats.py: CQC rank; In files: stats.py:146; */
/* stats.py: CQC rank; In files: stats.py:144; */
"Amateur" = "Amatör";
/* stats.py: CQC rank; In files: stats.py:147; */
/* stats.py: CQC rank; In files: stats.py:145; */
"Semi Professional" = "Semi Professional";
/* stats.py: CQC rank; In files: stats.py:148; */
/* stats.py: CQC rank; In files: stats.py:146; */
"Professional" = "Professional";
/* stats.py: CQC rank; In files: stats.py:149; */
/* stats.py: CQC rank; In files: stats.py:147; */
"Champion" = "Champion";
/* stats.py: CQC rank; In files: stats.py:150; */
/* stats.py: CQC rank; In files: stats.py:148; */
"Hero" = "Hero";
/* stats.py: CQC rank; In files: stats.py:151; */
/* stats.py: CQC rank; In files: stats.py:149; */
"Gladiator" = "Gladiator";
/* stats.py: Federation rank; In files: stats.py:157; */
/* stats.py: Federation rank; In files: stats.py:155; */
"Recruit" = "Recruit";
/* stats.py: Federation rank; In files: stats.py:158; */
/* stats.py: Federation rank; In files: stats.py:156; */
"Cadet" = "Cadet";
/* stats.py: Federation rank; In files: stats.py:159; */
/* stats.py: Federation rank; In files: stats.py:157; */
"Midshipman" = "Midshipman";
/* stats.py: Federation rank; In files: stats.py:160; */
/* stats.py: Federation rank; In files: stats.py:158; */
"Petty Officer" = "Petty Officer";
/* stats.py: Federation rank; In files: stats.py:161; */
/* stats.py: Federation rank; In files: stats.py:159; */
"Chief Petty Officer" = "Chief Petty Officer";
/* stats.py: Federation rank; In files: stats.py:162; */
/* stats.py: Federation rank; In files: stats.py:160; */
"Warrant Officer" = "Warrant Officer";
/* stats.py: Federation rank; In files: stats.py:163; */
/* stats.py: Federation rank; In files: stats.py:161; */
"Ensign" = "Ensign";
/* stats.py: Federation rank; In files: stats.py:164; */
/* stats.py: Federation rank; In files: stats.py:162; */
"Lieutenant" = "Lieutenant";
/* stats.py: Federation rank; In files: stats.py:165; */
/* stats.py: Federation rank; In files: stats.py:163; */
"Lieutenant Commander" = "Lieutenant Commander";
/* stats.py: Federation rank; In files: stats.py:166; */
/* stats.py: Federation rank; In files: stats.py:164; */
"Post Commander" = "Post Commander";
/* stats.py: Federation rank; In files: stats.py:167; */
/* stats.py: Federation rank; In files: stats.py:165; */
"Post Captain" = "Post Captain";
/* stats.py: Federation rank; In files: stats.py:168; */
/* stats.py: Federation rank; In files: stats.py:166; */
"Rear Admiral" = "Rear Admiral";
/* stats.py: Federation rank; In files: stats.py:169; */
/* stats.py: Federation rank; In files: stats.py:167; */
"Vice Admiral" = "Vice Admiral";
/* stats.py: Federation rank; In files: stats.py:170; */
/* stats.py: Federation rank; In files: stats.py:168; */
"Admiral" = "Admiral";
/* stats.py: Empire rank; In files: stats.py:176; */
/* stats.py: Empire rank; In files: stats.py:174; */
"Outsider" = "Outsider";
/* stats.py: Empire rank; In files: stats.py:177; */
/* stats.py: Empire rank; In files: stats.py:175; */
"Serf" = "Serf";
/* stats.py: Empire rank; In files: stats.py:179; */
/* stats.py: Empire rank; In files: stats.py:177; */
"Squire" = "Squire";
/* stats.py: Empire rank; In files: stats.py:180; */
/* stats.py: Empire rank; In files: stats.py:178; */
"Knight" = "Knight";
/* stats.py: Empire rank; In files: stats.py:181; */
/* stats.py: Empire rank; In files: stats.py:179; */
"Lord" = "Lord";
/* stats.py: Empire rank; In files: stats.py:182; */
/* stats.py: Empire rank; In files: stats.py:180; */
"Baron" = "Baron";
/* stats.py: Empire rank; In files: stats.py:183; */
/* stats.py: Empire rank; In files: stats.py:181; */
"Viscount" = "Viscount";
/* stats.py: Empire rank; In files: stats.py:184; */
/* stats.py: Empire rank; In files: stats.py:182; */
"Count" = "Count";
/* stats.py: Empire rank; In files: stats.py:185; */
/* stats.py: Empire rank; In files: stats.py:183; */
"Earl" = "Earl";
/* stats.py: Empire rank; In files: stats.py:186; */
/* stats.py: Empire rank; In files: stats.py:184; */
"Marquis" = "Marquis";
/* stats.py: Empire rank; In files: stats.py:187; */
/* stats.py: Empire rank; In files: stats.py:185; */
"Duke" = "Duke";
/* stats.py: Empire rank; In files: stats.py:188; */
/* stats.py: Empire rank; In files: stats.py:186; */
"Prince" = "Prince";
/* stats.py: Empire rank; In files: stats.py:189; */
/* stats.py: Empire rank; In files: stats.py:187; */
"King" = "King";
/* stats.py: Power rank; In files: stats.py:195; */
/* stats.py: Power rank; In files: stats.py:193; */
"Rating 1" = "Rating 1";
/* stats.py: Power rank; In files: stats.py:196; */
/* stats.py: Power rank; In files: stats.py:194; */
"Rating 2" = "Rating 2";
/* stats.py: Power rank; In files: stats.py:197; */
/* stats.py: Power rank; In files: stats.py:195; */
"Rating 3" = "Rating 3";
/* stats.py: Power rank; In files: stats.py:198; */
/* stats.py: Power rank; In files: stats.py:196; */
"Rating 4" = "Rating 4";
/* stats.py: Power rank; In files: stats.py:199; */
/* stats.py: Power rank; In files: stats.py:197; */
"Rating 5" = "Rating 5";
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:413; */
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:409; */
"Value" = "Värde";
/* stats.py: Status dialog title; In files: stats.py:422; */
/* stats.py: Status dialog title; In files: stats.py:418; */
"Ships" = "Skepp";
+317 -317
View File
@@ -1,575 +1,575 @@
/* Language name */
"!Language" = "Українська";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:212; eddn.py:584; eddn.py:925; load.py:215; load.py:593; load.py:940; eddn.py:252; eddn.py:699; eddn.py:1439; */
"Sending data to EDDN..." = "Відправка даних до EDDN...";
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:257; eddn.py:864; eddn.py:898; eddn.py:937; load.py:260; load.py:878; load.py:913; load.py:952; eddn.py:316; eddn.py:1375; eddn.py:1410; eddn.py:1451; */
"Error: Can't connect to EDDN" = "Помилка: Немає зв'язку з EDDN!";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:663; load.py:672; eddn.py:1122; */
"Send station data to the Elite Dangerous Data Network" = "Надсилати дані станцій до Elite Dangerous Data Network";
/* eddn.py: Enable EDDN support for system and other scan data checkbox label; In files: eddn.py:673; load.py:682; eddn.py:1133; */
"Send system and scan data to the Elite Dangerous Data Network" = "Відправляти систему і результати сканування в Elite Dangerous Data Network";
/* eddn.py: EDDN delay sending until docked option is on, this message notes that a send was skipped due to this; In files: eddn.py:683; load.py:692; eddn.py:1144; */
"Delay sending until docked" = "Відкласти відправку даних до стикування";
/* edsm.py: Settings>EDSM - Label on checkbox for 'send data'; In files: edsm.py:197; load.py:197; edsm.py:237; */
"Send flight log and Cmdr status to EDSM" = "Відправляти дані бортового журналу до EDSM";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:206; load.py:206; edsm.py:247; */
"Elite Dangerous Star Map credentials" = "Дані Elite Dangerous Star Map";
/* edsm.py: Game Commander name label in EDSM settings; theme.py: Label for commander name in main window; EDMarketConnector.py: Label for commander name in main window; stats.py: Cmdr stats; In files: edsm.py:216; load.py:216; edsm.py:258; theme.py:227; EDMarketConnector.py:822; stats.py:59; */
"Cmdr" = "Км-др";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:223; load.py:223; edsm.py:266; */
"Commander Name" = "Ім`я пілота";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:230; inara.py:233; load.py:230; load.py:233; edsm.py:274; inara.py:243; */
"API Key" = "API Ключ";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:256; load.py:256; edsm.py:301; prefs.py:520; prefs.py:1174; prefs.py:1207; stats.py:156; stats.py:175; stats.py:194; stats.py:211; */
"None" = "Нічого";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:622; edsm.py:724; load.py:627; load.py:729; edsm.py:747; edsm.py:875; */
"Error: EDSM {MSG}" = "Помилка: EDSM {MSG}!";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:658; edsm.py:720; load.py:663; load.py:725; edsm.py:784; edsm.py:870; */
"Error: Can't connect to EDSM" = "Помилка: Немає з`єднання з EDSM!";
/* inara.py: Checkbox to enable INARA API Usage; In files: inara.py:215; load.py:215; inara.py:222; */
"Send flight log and Cmdr status to Inara" = "Відправляти дані бортового журналу до Inara";
/* inara.py: Text for INARA API keys link ( goes to https://inara.cz/settings-api ); In files: inara.py:225; load.py:225; inara.py:234; */
"Inara credentials" = "Обліковий запис Inara";
/* inara.py: INARA API returned some kind of error (error message will be contained in {MSG}); In files: inara.py:1316; inara.py:1328; load.py:1319; load.py:1331; inara.py:1587; inara.py:1600; */
"Error: Inara {MSG}" = "Помилка: Inara {MSG}";
/* EDMarketConnector.py: No data was returned for the commander from the Frontier CAPI; In files: test_ast_stuff.py:2; EDMarketConnector.py:1047; */
"CAPI: No commander data returned" = "CAPI: Не отримано данних пілота";
/* journal_lock.py: Title text on popup when Journal directory already locked; In files: journal_lock.py:206; */
"Journal directory already locked" = "Каталог журналу вже заблокований";
/* journal_lock.py: Text for when newly selected Journal directory is already locked; In files: journal_lock.py:223:224; */
"The new Journal Directory location is already locked.{CR}You can either attempt to resolve this and then Retry, or choose to Ignore this." = "Нове розташування каталогу журналів уже заблоковано. {CR} Ви можете спробувати вирішити це, а потім повторити спробу, або Ігнорувати це.";
/* journal_lock.py: Generic 'Retry' button label; In files: journal_lock.py:228; */
"Retry" = "Повторна спроба";
/* journal_lock.py: Generic 'Ignore' button label; In files: journal_lock.py:232; */
"Ignore" = "Ігнорувати";
/* ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; In files: ttkHyperlinkLabel.py:42; EDMarketConnector.py:866; */
"Copy" = "Копіювати";
/* EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:529; EDMarketConnector.py:828; EDMarketConnector.py:1520; */
"Update" = "Оновлення";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:612; prefs.py:867; */
"Always on top" = "Поверх інших вікон";
/* EDMarketConnector.py: Unknown suit; In files: EDMarketConnector.py:746; */
"Unknown" = "Невідомо";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:824; EDMarketConnector.py:1279; */
"Role" = "Посада";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:824; EDMarketConnector.py:1289; EDMarketConnector.py:1312; stats.py:409; */
"Ship" = "Корабель";
/* EDMarketConnector.py: Label for 'Suit' line in main UI; In files: EDMarketConnector.py:825; */
"Suit" = "Костюм";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:826; prefs.py:607; stats.py:411; */
"System" = "Система";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:827; prefs.py:625; prefs.py:762; stats.py:412; */
"Station" = "Станція";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:830; EDMarketConnector.py:845; EDMarketConnector.py:848; EDMarketConnector.py:2019; */
"File" = "Файл";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:831; EDMarketConnector.py:846; EDMarketConnector.py:849; */
"Edit" = "Редагувати";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:832; */
"View" = "Вид";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:833; */
"Window" = "Вікно";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:834; EDMarketConnector.py:847; EDMarketConnector.py:850; */
"Help" = "Довідка";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:837; EDMarketConnector.py:863; EDMarketConnector.py:1566; */
"About {APP}" = "Про {APP}";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:839; EDMarketConnector.py:862; */
"Check for Updates..." = "Перевірка оновлень...";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:840; EDMarketConnector.py:854; */
"Save Raw Data..." = "Зберегти необроблені дані...";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:841; EDMarketConnector.py:853; stats.py:406; */
"Status" = "Статус";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:842; EDMarketConnector.py:860; */
"Privacy Policy" = "Політика конфіденційності";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:843; EDMarketConnector.py:861; EDMarketConnector.py:1600; */
"Release Notes" = "Інформація про реліз";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:855; EDMarketConnector.py:2020; prefs.py:255; */
"Settings" = "Налаштування";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:856; */
"Exit" = "Вихід";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:859; */
"Documentation" = "Документація";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:872; */
"Logging in..." = "Вхід в...";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:888; EDMarketConnector.py:1431; */
"Authentication successful" = "Автентифікацію пройдено успішно";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:919; */
"You're not docked at a station!" = "Ви не стиковані з станцією!";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:927; */
"Station doesn't have anything!" = "На станції немає нічого!";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:932; */
"Station doesn't have a market!" = "На станції немає ринку!";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1006; */
"Fetching data..." = "Отримання даних...";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1051; stats.py:335; */
"Who are you?!" = "Хто ти?!";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1057; stats.py:345; */
"Where are you?!" = "Де ми?!";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1064; stats.py:353; */
"What are you flying?!" = "На чому летимо?!";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1226; */
"Last updated at %H:%M:%S" = "Останнє оновлення було %H:%M:%S";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1254; */
"Fighter" = "Винищувач";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1255; */
"Gunner" = "Стрілець";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1256; */
"Helm" = "Керівник";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1514; */
"cooldown {SS}s" = "очікування {SS} сек";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1626; prefs.py:306; */
"OK" = "Згода";
/* EDMarketConnector.py: The application is shutting down; In files: EDMarketConnector.py:1693; */
"Shutting down..." = "Закриття...";
/* EDMarketConnector.py: Popup-text about 'active' plugins without Python 3.x support; In files: EDMarketConnector.py:2008:2014; */
"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." = "Один або кілька увімкнених плагінів ще не підтримують Python 3.x. Перегляньте список на вкладці '{PLUGINS}' в '{FILE}' > '{SETTINGS}'. Ви повинні перевірити, чи є в наявності оновлена ​​версія, інакше повідомити розробника плагіну про те, що їм потрібно оновити код для Python 3.x.\n\nВи можете відключити плагін, перейменувавши його папку додавши '{DISABLED}' в кінці назви.";
/* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2018; prefs.py:978; */
"Plugins" = "Плагіни";
/* EDMarketConnector.py: Popup window title for list of 'enabled' plugins that don't work with Python 3.x; In files: EDMarketConnector.py:2029; */
"EDMC: Plugins Without Python 3.x Support" = "EDMC: Плагіни без підтримки Python 3.x!";
/* companion.py: Frontier CAPI data doesn't agree with latest Journal game location; In files: companion.py:237; */
/* companion.py: Frontier CAPI data doesn't agree with latest Journal game location; In files: companion.py:245; */
"Error: Frontier server is lagging" = "Помилка: З`єднання з сервером гри з затримками!";
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:263; */
/* companion.py: Generic "something went wrong with Frontier Auth" error; In files: companion.py:271; */
"Error: Invalid Credentials" = "Помилка: Невірні облікові дані!";
/* companion.py: Frontier CAPI authorisation not for currently game-active commander; In files: companion.py:288; */
/* companion.py: Frontier CAPI authorisation not for currently game-active commander; In files: companion.py:296; */
"Error: Wrong Cmdr" = "Помилка! Не той обліковий запис!";
/* companion.py: Generic error prefix - following text is from Frontier auth service; In files: companion.py:414; companion.py:499; */
/* companion.py: Generic error prefix - following text is from Frontier auth service; In files: companion.py:432; companion.py:517; */
"Error" = "Помилка";
/* companion.py: Frontier auth, no 'usr' section in returned data; companion.py: Frontier auth, no 'customer_id' in 'usr' section in returned data; In files: companion.py:457; companion.py:462; */
/* companion.py: Frontier auth, no 'usr' section in returned data; companion.py: Frontier auth, no 'customer_id' in 'usr' section in returned data; In files: companion.py:475; companion.py:480; */
"Error: Couldn't check token customer_id" = "Помилка: Не вдалося перевірити токен customer_id";
/* companion.py: Frontier auth customer_id doesn't match game session FID; In files: companion.py:468; */
/* companion.py: Frontier auth customer_id doesn't match game session FID; In files: companion.py:486; */
"Error: customer_id doesn't match!" = "Error: customer_id не співпадає!";
/* companion.py: Failed to get Access Token from Frontier Auth service; In files: companion.py:490; */
/* companion.py: Failed to get Access Token from Frontier Auth service; In files: companion.py:508; */
"Error: unable to get token" = "Помилка: не вдалося отримати токен";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:194; prefs.py:468; prefs.py:702; prefs.py:735; */
/* EDMarketConnector.py: Main UI Update button; EDMarketConnector.py: Update button in main window; In files: EDMarketConnector.py:601; EDMarketConnector.py:919; EDMarketConnector.py:1748; */
"Update" = "Оновлення";
/* EDMarketConnector.py: Appearance - Label for checkbox to select if application always on top; prefs.py: Appearance - Label for checkbox to select if application always on top; In files: EDMarketConnector.py:710; prefs.py:875; */
"Always on top" = "Поверх інших вікон";
/* EDMarketConnector.py: Unknown suit; In files: EDMarketConnector.py:837; */
"Unknown" = "Невідомо";
/* EDMarketConnector.py: Label for commander name in main window; edsm.py: Game Commander name label in EDSM settings; stats.py: Cmdr stats; theme.py: Label for commander name in main window; In files: EDMarketConnector.py:913; edsm.py:332; stats.py:57; theme.py:290; */
"Cmdr" = "Км-др";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: Multicrew role label in main window; In files: EDMarketConnector.py:915; EDMarketConnector.py:1487; */
"Role" = "Посада";
/* EDMarketConnector.py: 'Ship' or multi-crew role label in main window, as applicable; EDMarketConnector.py: 'Ship' label in main UI; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:915; EDMarketConnector.py:1497; EDMarketConnector.py:1520; stats.py:405; */
"Ship" = "Корабель";
/* EDMarketConnector.py: Label for 'Suit' line in main UI; In files: EDMarketConnector.py:916; */
"Suit" = "Костюм";
/* EDMarketConnector.py: Label for 'System' line in main UI; prefs.py: Configuration - Label for selection of 'System' provider website; stats.py: Main window; In files: EDMarketConnector.py:917; prefs.py:614; stats.py:407; */
"System" = "Система";
/* EDMarketConnector.py: Label for 'Station' line in main UI; prefs.py: Configuration - Label for selection of 'Station' provider website; prefs.py: Appearance - Example 'Normal' text; stats.py: Status dialog subtitle; In files: EDMarketConnector.py:918; prefs.py:632; prefs.py:770; stats.py:408; */
"Station" = "Станція";
/* EDMarketConnector.py: 'File' menu title on OSX; EDMarketConnector.py: 'File' menu title; EDMarketConnector.py: 'File' menu; In files: EDMarketConnector.py:921; EDMarketConnector.py:939; EDMarketConnector.py:942; EDMarketConnector.py:2264; */
"File" = "Файл";
/* EDMarketConnector.py: 'Edit' menu title on OSX; EDMarketConnector.py: 'Edit' menu title; In files: EDMarketConnector.py:922; EDMarketConnector.py:940; EDMarketConnector.py:943; */
"Edit" = "Редагувати";
/* EDMarketConnector.py: 'View' menu title on OSX; In files: EDMarketConnector.py:923; */
"View" = "Вид";
/* EDMarketConnector.py: 'Window' menu title on OSX; In files: EDMarketConnector.py:924; */
"Window" = "Вікно";
/* EDMarketConnector.py: Help' menu title on OSX; EDMarketConnector.py: 'Help' menu title; In files: EDMarketConnector.py:925; EDMarketConnector.py:941; EDMarketConnector.py:944; */
"Help" = "Довідка";
/* EDMarketConnector.py: App menu entry on OSX; EDMarketConnector.py: Help > About App; In files: EDMarketConnector.py:928; EDMarketConnector.py:959; EDMarketConnector.py:1804; */
"About {APP}" = "Про {APP}";
/* EDMarketConnector.py: Help > Check for Updates...; In files: EDMarketConnector.py:930; EDMarketConnector.py:958; */
"Check for Updates..." = "Перевірка оновлень...";
/* EDMarketConnector.py: File > Save Raw Data...; In files: EDMarketConnector.py:931; EDMarketConnector.py:948; */
"Save Raw Data..." = "Зберегти необроблені дані...";
/* EDMarketConnector.py: File > Status; stats.py: Status dialog title; In files: EDMarketConnector.py:932; EDMarketConnector.py:947; stats.py:402; */
"Status" = "Статус";
/* EDMarketConnector.py: Help > Documentation; In files: EDMarketConnector.py:933; EDMarketConnector.py:953; */
"Documentation" = "Документація";
/* EDMarketConnector.py: Help > Privacy Policy; In files: EDMarketConnector.py:936; EDMarketConnector.py:956; */
"Privacy Policy" = "Політика конфіденційності";
/* EDMarketConnector.py: Help > Release Notes; In files: EDMarketConnector.py:937; EDMarketConnector.py:957; EDMarketConnector.py:1838; */
"Release Notes" = "Інформація про реліз";
/* EDMarketConnector.py: File > Settings; prefs.py: File > Settings (macOS); In files: EDMarketConnector.py:949; EDMarketConnector.py:2265; prefs.py:241; */
"Settings" = "Налаштування";
/* EDMarketConnector.py: File > Exit; In files: EDMarketConnector.py:950; */
"Exit" = "Вихід";
/* EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; In files: EDMarketConnector.py:962; ttkHyperlinkLabel.py:53; */
"Copy" = "Копіювати";
/* EDMarketConnector.py: Status - Attempting to get a Frontier Auth Access Token; In files: EDMarketConnector.py:978; */
"Logging in..." = "Вхід в...";
/* EDMarketConnector.py: Successfully authenticated with the Frontier website; In files: EDMarketConnector.py:994; EDMarketConnector.py:1657; */
"Authentication successful" = "Автентифікацію пройдено успішно";
/* EDMarketConnector.py: Player is not docked at a station, when we expect them to be; In files: EDMarketConnector.py:1025; */
"You're not docked at a station!" = "Ви не стиковані з станцією!";
/* EDMarketConnector.py: Status - Either no market or no modules data for station from Frontier CAPI; In files: EDMarketConnector.py:1033; */
"Station doesn't have anything!" = "На станції немає нічого!";
/* EDMarketConnector.py: Status - No station market data from Frontier CAPI; In files: EDMarketConnector.py:1038; */
"Station doesn't have a market!" = "На станції немає ринку!";
/* EDMarketConnector.py: Status - Attempting to retrieve data from Frontier CAPI; In files: EDMarketConnector.py:1128; EDMarketConnector.py:1179; */
"Fetching data..." = "Отримання даних...";
/* EDMarketConnector.py: No data was returned for the commander from the Frontier CAPI; In files: EDMarketConnector.py:1242; */
"CAPI: No commander data returned" = "CAPI: Не отримано данних пілота";
/* EDMarketConnector.py: We didn't have the commander name when we should have; stats.py: Unknown commander; In files: EDMarketConnector.py:1246; stats.py:333; */
"Who are you?!" = "Хто ти?!";
/* EDMarketConnector.py: We don't know where the commander is, when we should; stats.py: Unknown location; In files: EDMarketConnector.py:1252; stats.py:341; */
"Where are you?!" = "Де ми?!";
/* EDMarketConnector.py: We don't know what ship the commander is in, when we should; stats.py: Unknown ship; In files: EDMarketConnector.py:1259; stats.py:349; */
"What are you flying?!" = "На чому летимо?!";
/* EDMarketConnector.py: Time when we last obtained Frontier CAPI data; In files: EDMarketConnector.py:1434; */
"Last updated at %H:%M:%S" = "Останнє оновлення було %H:%M:%S";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1462; */
"Fighter" = "Винищувач";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1463; */
"Gunner" = "Стрілець";
/* EDMarketConnector.py: Multicrew role; In files: EDMarketConnector.py:1464; */
"Helm" = "Керівник";
/* EDMarketConnector.py: Cooldown on 'Update' button; In files: EDMarketConnector.py:1742; */
"cooldown {SS}s" = "очікування {SS} сек";
/* EDMarketConnector.py: Generic 'OK' button label; prefs.py: 'OK' button on Settings/Preferences window; In files: EDMarketConnector.py:1864; prefs.py:292; */
"OK" = "Згода";
/* EDMarketConnector.py: The application is shutting down; In files: EDMarketConnector.py:1936; */
"Shutting down..." = "Закриття...";
/* EDMarketConnector.py: Popup-text about 'active' plugins without Python 3.x support; In files: EDMarketConnector.py:2253:2259; */
"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." = "Один або кілька увімкнених плагінів ще не підтримують Python 3.x. Перегляньте список на вкладці '{PLUGINS}' в '{FILE}' > '{SETTINGS}'. Ви повинні перевірити, чи є в наявності оновлена ​​версія, інакше повідомити розробника плагіну про те, що їм потрібно оновити код для Python 3.x.\n\nВи можете відключити плагін, перейменувавши його папку додавши '{DISABLED}' в кінці назви.";
/* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2263; prefs.py:986; */
"Plugins" = "Плагіни";
/* EDMarketConnector.py: Popup window title for list of 'enabled' plugins that don't work with Python 3.x; In files: EDMarketConnector.py:2274; */
"EDMC: Plugins Without Python 3.x Support" = "EDMC: Плагіни без підтримки Python 3.x!";
/* journal_lock.py: Title text on popup when Journal directory already locked; In files: journal_lock.py:208; */
"Journal directory already locked" = "Каталог журналу вже заблокований";
/* journal_lock.py: Text for when newly selected Journal directory is already locked; In files: journal_lock.py:225:226; */
"The new Journal Directory location is already locked.{CR}You can either attempt to resolve this and then Retry, or choose to Ignore this." = "Нове розташування каталогу журналів уже заблоковано. {CR} Ви можете спробувати вирішити це, а потім повторити спробу, або Ігнорувати це.";
/* journal_lock.py: Generic 'Retry' button label; In files: journal_lock.py:230; */
"Retry" = "Повторна спроба";
/* journal_lock.py: Generic 'Ignore' button label; In files: journal_lock.py:234; */
"Ignore" = "Ігнорувати";
/* l10n.py: The system default language choice in Settings > Appearance; prefs.py: Settings > Configuration - Label on 'reset journal files location to default' button; prefs.py: The system default language choice in Settings > Appearance; prefs.py: Label for 'Default' theme radio button; In files: l10n.py:193; prefs.py:455; prefs.py:709; prefs.py:742; */
"Default" = "Стандартне налаштування";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:251; */
/* eddn.py: Error while trying to send data to EDDN; In files: eddn.py:458; eddn.py:2413; eddn.py:2451; eddn.py:2519; */
"Error: Can't connect to EDDN" = "Помилка: Немає зв'язку з EDDN!";
/* eddn.py: Enable EDDN support for station data checkbox label; In files: eddn.py:2041; */
"Send station data to the Elite Dangerous Data Network" = "Надсилати дані станцій до Elite Dangerous Data Network";
/* eddn.py: Enable EDDN support for system and other scan data checkbox label; In files: eddn.py:2052; */
"Send system and scan data to the Elite Dangerous Data Network" = "Відправляти систему і результати сканування в Elite Dangerous Data Network";
/* eddn.py: EDDN delay sending until docked option is on, this message notes that a send was skipped due to this; In files: eddn.py:2063; */
"Delay sending until docked" = "Відкласти відправку даних до стикування";
/* eddn.py: Status text shown while attempting to send data; In files: eddn.py:2507; */
"Sending data to EDDN..." = "Відправка даних до EDDN...";
/* edsm.py: Settings>EDSM - Label on checkbox for 'send data'; In files: edsm.py:308; */
"Send flight log and Cmdr status to EDSM" = "Відправляти дані бортового журналу до EDSM";
/* edsm.py: Settings>EDSM - Label on header/URL to EDSM API key page; In files: edsm.py:319; */
"Elite Dangerous Star Map credentials" = "Дані Elite Dangerous Star Map";
/* edsm.py: EDSM Commander name label in EDSM settings; In files: edsm.py:341; */
"Commander Name" = "Ім`я пілота";
/* edsm.py: EDSM API key label; inara.py: Inara API key label; In files: edsm.py:350; inara.py:278; */
"API Key" = "API Ключ";
/* edsm.py: We have no data on the current commander; prefs.py: No hotkey/shortcut set; stats.py: No rank; In files: edsm.py:394; prefs.py:527; prefs.py:1157; prefs.py:1190; stats.py:154; stats.py:173; stats.py:192; stats.py:209; */
"None" = "Нічого";
/* edsm.py: EDSM Plugin - Error message from EDSM API; In files: edsm.py:916; edsm.py:1048; */
"Error: EDSM {MSG}" = "Помилка: EDSM {MSG}!";
/* edsm.py: EDSM Plugin - Error connecting to EDSM API; In files: edsm.py:953; edsm.py:1043; */
"Error: Can't connect to EDSM" = "Помилка: Немає з`єднання з EDSM!";
/* inara.py: Checkbox to enable INARA API Usage; In files: inara.py:257; */
"Send flight log and Cmdr status to Inara" = "Відправляти дані бортового журналу до Inara";
/* inara.py: Text for INARA API keys link ( goes to https://inara.cz/settings-api ); In files: inara.py:269; */
"Inara credentials" = "Обліковий запис Inara";
/* inara.py: INARA API returned some kind of error (error message will be contained in {MSG}); In files: inara.py:1650; inara.py:1663; */
"Error: Inara {MSG}" = "Помилка: Inara {MSG}";
/* prefs.py: File > Preferences menu entry for macOS; In files: prefs.py:237; */
"Preferences" = "Уподобання";
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:348; */
/* prefs.py: Settings > Output - choosing what data to save to files; In files: prefs.py:335; */
"Please choose what data to save" = "Оберіть дані які буде збережено";
/* prefs.py: Settings > Output option; In files: prefs.py:354; */
/* prefs.py: Settings > Output option; In files: prefs.py:341; */
"Market data in CSV format file" = "Ринкові дані у форматі CSV файлу";
/* prefs.py: Settings > Output option; In files: prefs.py:363; */
/* prefs.py: Settings > Output option; In files: prefs.py:350; */
"Market data in Trade Dangerous format file" = "Ринкові дані у форматі Trade Dangerous файлу";
/* prefs.py: Settings > Output option; In files: prefs.py:373; */
/* prefs.py: Settings > Output option; In files: prefs.py:360; */
"Ship loadout" = "Технічні дані корабля";
/* prefs.py: Settings > Output option; In files: prefs.py:383; */
/* prefs.py: Settings > Output option; In files: prefs.py:370; */
"Automatically update on docking" = "Автоматичне оновлювання при стикуванні зі станцією";
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:392; prefs.py:411; */
/* prefs.py: Settings > Output - Label for "where files are located"; In files: prefs.py:379; prefs.py:398; */
"File location" = "Розташування файлу";
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:400; prefs.py:450; */
/* prefs.py: macOS Preferences - files location selection button; In files: prefs.py:387; prefs.py:437; */
"Change..." = "Внесення змін...";
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:403; prefs.py:453; */
/* prefs.py: NOT-macOS Settings - files location selection button; prefs.py: NOT-macOS Setting - files location selection button; In files: prefs.py:390; prefs.py:440; */
"Browse..." = "Перегляд...";
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:418; */
/* prefs.py: Label for 'Output' Settings/Preferences tab; In files: prefs.py:405; */
"Output" = "Вивід";
/* prefs.py: Settings > Configuration - Label for Journal files location; In files: prefs.py:444; prefs.py:459; */
/* prefs.py: Settings > Configuration - Label for Journal files location; In files: prefs.py:431; prefs.py:446; */
"E:D journal file location" = "Розташування файлу-журналу E:D";
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:484; */
/* prefs.py: Hotkey/Shortcut settings prompt on OSX; In files: prefs.py:490; */
"Keyboard shortcut" = "Сполучення клавіш";
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:486; */
/* prefs.py: Hotkey/Shortcut settings prompt on Windows; In files: prefs.py:492; */
"Hotkey" = "Гаряча клавіша";
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:495; */
/* prefs.py: macOS Preferences > Configuration - restart the app message; In files: prefs.py:501; */
"Re-start {APP} to use shortcuts" = "Перезавантажте {APP} для використання ярликів";
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:504; */
/* prefs.py: macOS - Configuration - need to grant the app permission for keyboard shortcuts; In files: prefs.py:510; */
"{APP} needs permission to use shortcuts" = "{APP} необхідні дозволи для використання ярликів";
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:509; */
/* prefs.py: Shortcut settings button on OSX; In files: prefs.py:515; */
"Open System Preferences" = "Відкрити налаштування системи";
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:531; */
/* prefs.py: Configuration - Act on hotkey only when ED is in foreground; In files: prefs.py:538; */
"Only when Elite: Dangerous is the active app" = "Тільки тоді, коли Elite: Dangerous є активним додатком";
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:542; */
/* prefs.py: Configuration - play sound when hotkey used; In files: prefs.py:549; */
"Play sound" = "Грати звук";
/* prefs.py: Configuration - disable checks for app updates when in-game; In files: prefs.py:557; */
/* prefs.py: Configuration - disable checks for app updates when in-game; In files: prefs.py:564; */
"Disable Automatic Application Updates Check when in-game" = "Вимкнути автоматичні перевірки оновлення додатку, коли в грі";
/* prefs.py: Label for preferred shipyard, system and station 'providers'; In files: prefs.py:570; */
/* prefs.py: Label for preferred shipyard, system and station 'providers'; In files: prefs.py:577; */
"Preferred websites" = "Обрані веб-сайти";
/* prefs.py: Label for Shipyard provider selection; In files: prefs.py:581; */
/* prefs.py: Label for Shipyard provider selection; In files: prefs.py:588; */
"Shipyard" = "Корабельна верф";
/* prefs.py: Label for checkbox to utilise alternative Coriolis URL method; In files: prefs.py:593; */
/* prefs.py: Label for checkbox to utilise alternative Coriolis URL method; In files: prefs.py:600; */
"Use alternate URL method" = "Використовувати альтернативний URL-метод";
/* prefs.py: Configuration - Label for selection of Log Level; In files: prefs.py:646; */
/* prefs.py: Configuration - Label for selection of Log Level; In files: prefs.py:653; */
"Log Level" = "Рівень подробиць журналу";
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:674; */
/* prefs.py: Label for 'Configuration' tab in Settings; In files: prefs.py:681; */
"Configuration" = "Конфігурація";
/* prefs.py: Preferences privacy tab title; In files: prefs.py:696; */
/* prefs.py: Preferences privacy tab title; In files: prefs.py:703; */
"Privacy" = "Конфіденційність";
/* prefs.py: Label for Settings > Appeareance > selection of 'normal' text colour; In files: prefs.py:709; */
/* prefs.py: Label for Settings > Appeareance > selection of 'normal' text colour; In files: prefs.py:716; */
"Normal text" = "Нормальний текст";
/* prefs.py: Label for Settings > Appeareance > selection of 'highlightes' text colour; In files: prefs.py:711; */
/* prefs.py: Label for Settings > Appeareance > selection of 'highlightes' text colour; In files: prefs.py:718; */
"Highlighted text" = "Виділений текст";
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:720; */
/* prefs.py: Appearance - Label for selection of application display language; In files: prefs.py:727; */
"Language" = "Мова";
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:730; */
/* prefs.py: Label for Settings > Appearance > Theme selection; In files: prefs.py:737; */
"Theme" = "Тема";
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:741; */
/* prefs.py: Label for 'Dark' theme radio button; In files: prefs.py:749; */
"Dark" = "Темний";
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:748; */
/* prefs.py: Label for 'Transparent' theme radio button; In files: prefs.py:756; */
"Transparent" = "Прозора";
/* prefs.py: Appearance - Label for selection of UI scaling; In files: prefs.py:794; */
/* prefs.py: Appearance - Label for selection of UI scaling; In files: prefs.py:802; */
"UI Scale Percentage" = "Відсоток шкали інтерфейсу користувача";
/* prefs.py: Appearance - Help/hint text for UI scaling selection; In files: prefs.py:815; */
/* prefs.py: Appearance - Help/hint text for UI scaling selection; In files: prefs.py:823; */
"100 means Default{CR}Restart Required for{CR}changes to take effect!" = "100 означає, що {CR} потрібно перезапустити за замовчуванням, щоб зміни вступили в силу {CR}!";
/* prefs.py: Appearance - Label for selection of main window transparency; In files: prefs.py:825; */
/* prefs.py: Appearance - Label for selection of main window transparency; In files: prefs.py:833; */
"Main window transparency" = "Прозорість головного вікна";
/* prefs.py: Appearance - Help/hint text for Main window transparency selection; In files: prefs.py:845:848; */
/* prefs.py: Appearance - Help/hint text for Main window transparency selection; In files: prefs.py:853:856; */
"100 means fully opaque.{CR}Window is updated in real time" = "100 означає повністю непрозоре. {CR} Вікно буде оновлюватись в режимі реального часу";
/* prefs.py: Appearance option for Windows "minimize to system tray"; In files: prefs.py:877; */
/* prefs.py: Appearance option for Windows "minimize to system tray"; In files: prefs.py:885; */
"Minimize to system tray" = "Згорнути програму";
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:885; */
/* prefs.py: Label for Settings > Appearance tab; In files: prefs.py:893; */
"Appearance" = "Зовнішній вигляд";
/* prefs.py: Label for location of third-party plugins folder; In files: prefs.py:900; */
/* prefs.py: Label for location of third-party plugins folder; In files: prefs.py:908; */
"Plugins folder" = "Сховище плагінів";
/* prefs.py: Label on button used to open a filesystem folder; In files: prefs.py:907; */
/* prefs.py: Label on button used to open a filesystem folder; In files: prefs.py:915; */
"Open" = "Відкрити";
/* prefs.py: Tip/label about how to disable plugins; In files: prefs.py:915; */
/* prefs.py: Tip/label about how to disable plugins; In files: prefs.py:923; */
"Tip: You can disable a plugin by{CR}adding '{EXT}' to its folder name" = "Підказка: Ви можете відключити плагін через {CR} додання '{EXT}' до назви папки";
/* prefs.py: Label on list of enabled plugins; In files: prefs.py:926; */
/* prefs.py: Label on list of enabled plugins; In files: prefs.py:934; */
"Enabled Plugins" = "Увімкнені плагіни";
/* prefs.py: Plugins - Label for list of 'enabled' plugins that don't work with Python 3.x; In files: prefs.py:946; */
/* prefs.py: Plugins - Label for list of 'enabled' plugins that don't work with Python 3.x; In files: prefs.py:954; */
"Plugins Without Python 3.x Support:" = "Плагіни без підтримки Python 3.x:";
/* prefs.py: Plugins - Label on URL to documentation about migrating plugins from Python 2.7; In files: prefs.py:954; */
/* prefs.py: Plugins - Label on URL to documentation about migrating plugins from Python 2.7; In files: prefs.py:962; */
"Information on migrating plugins" = "Інформація про міграцію плагінів";
/* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:969; */
/* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:977; */
"Disabled Plugins" = "Вимкнені плагіни";
/* stats.py: Cmdr stats; In files: stats.py:60; */
/* stats.py: Cmdr stats; In files: stats.py:58; */
"Balance" = "Баланс";
/* stats.py: Cmdr stats; In files: stats.py:61; */
/* stats.py: Cmdr stats; In files: stats.py:59; */
"Loan" = "Борг";
/* stats.py: Top rank; In files: stats.py:65; */
/* stats.py: Top rank; In files: stats.py:63; */
"Elite" = "Еліта";
/* stats.py: Ranking; In files: stats.py:76; */
/* stats.py: Ranking; In files: stats.py:74; */
"Combat" = "Бойовий";
/* stats.py: Ranking; In files: stats.py:77; */
/* stats.py: Ranking; In files: stats.py:75; */
"Trade" = "Торговий";
/* stats.py: Ranking; In files: stats.py:78; */
/* stats.py: Ranking; In files: stats.py:76; */
"Explorer" = "Дослідницький";
/* stats.py: Ranking; In files: stats.py:81; */
/* stats.py: Ranking; In files: stats.py:79; */
"CQC" = "Близький бій (CQC)";
/* stats.py: Ranking; In files: stats.py:82; */
/* stats.py: Ranking; In files: stats.py:80; */
"Federation" = "Федерація";
/* stats.py: Ranking; In files: stats.py:83; */
/* stats.py: Ranking; In files: stats.py:81; */
"Empire" = "Імперія";
/* stats.py: Ranking; In files: stats.py:84; */
/* stats.py: Ranking; In files: stats.py:82; */
"Powerplay" = "Змагання";
/* stats.py: Combat rank; In files: stats.py:93; */
/* stats.py: Combat rank; In files: stats.py:91; */
"Harmless" = "Безпечний";
/* stats.py: Combat rank; In files: stats.py:94; */
/* stats.py: Combat rank; In files: stats.py:92; */
"Mostly Harmless" = "Здебільшого Безпечний";
/* stats.py: Combat rank; In files: stats.py:95; */
/* stats.py: Combat rank; In files: stats.py:93; */
"Novice" = "Новачок";
/* stats.py: Combat rank; In files: stats.py:96; */
/* stats.py: Combat rank; In files: stats.py:94; */
"Competent" = "Компетентний";
/* stats.py: Combat rank; In files: stats.py:97; */
/* stats.py: Combat rank; In files: stats.py:95; */
"Expert" = "Експерт";
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:98; stats.py:178; */
/* stats.py: Combat rank; stats.py: Empire rank; In files: stats.py:96; stats.py:176; */
"Master" = "Майстер";
/* stats.py: Combat rank; In files: stats.py:99; */
/* stats.py: Combat rank; In files: stats.py:97; */
"Dangerous" = "Небезпечний";
/* stats.py: Combat rank; In files: stats.py:100; */
/* stats.py: Combat rank; In files: stats.py:98; */
"Deadly" = "Убивчий";
/* stats.py: Trade rank; In files: stats.py:103; */
/* stats.py: Trade rank; In files: stats.py:101; */
"Penniless" = "Злидень";
/* stats.py: Trade rank; In files: stats.py:104; */
/* stats.py: Trade rank; In files: stats.py:102; */
"Mostly Penniless" = "Здебільшого Нужденний";
/* stats.py: Trade rank; In files: stats.py:105; */
/* stats.py: Trade rank; In files: stats.py:103; */
"Peddler" = "Штовхач";
/* stats.py: Trade rank; In files: stats.py:106; */
/* stats.py: Trade rank; In files: stats.py:104; */
"Dealer" = "Дилер";
/* stats.py: Trade rank; In files: stats.py:107; */
/* stats.py: Trade rank; In files: stats.py:105; */
"Merchant" = "Торговець";
/* stats.py: Trade rank; In files: stats.py:108; */
/* stats.py: Trade rank; In files: stats.py:106; */
"Broker" = "Брокер";
/* stats.py: Trade rank; In files: stats.py:109; */
/* stats.py: Trade rank; In files: stats.py:107; */
"Entrepreneur" = "Підприємець";
/* stats.py: Trade rank; In files: stats.py:110; */
/* stats.py: Trade rank; In files: stats.py:108; */
"Tycoon" = "Магнат";
/* stats.py: Explorer rank; In files: stats.py:113; */
/* stats.py: Explorer rank; In files: stats.py:111; */
"Aimless" = "Безцільний";
/* stats.py: Explorer rank; In files: stats.py:114; */
/* stats.py: Explorer rank; In files: stats.py:112; */
"Mostly Aimless" = "Здебільшого Безцільний";
/* stats.py: Explorer rank; In files: stats.py:115; */
/* stats.py: Explorer rank; In files: stats.py:113; */
"Scout" = "Розвідник";
/* stats.py: Explorer rank; In files: stats.py:116; */
/* stats.py: Explorer rank; In files: stats.py:114; */
"Surveyor" = "Геодезист";
/* stats.py: Explorer rank; In files: stats.py:117; */
/* stats.py: Explorer rank; In files: stats.py:115; */
"Trailblazer" = "Новатор";
/* stats.py: Explorer rank; In files: stats.py:118; */
/* stats.py: Explorer rank; In files: stats.py:116; */
"Pathfinder" = "Слідопит";
/* stats.py: Explorer rank; In files: stats.py:119; */
/* stats.py: Explorer rank; In files: stats.py:117; */
"Ranger" = "Рейнджер";
/* stats.py: Explorer rank; In files: stats.py:120; */
/* stats.py: Explorer rank; In files: stats.py:118; */
"Pioneer" = "Піонер";
/* stats.py: CQC rank; In files: stats.py:144; */
/* stats.py: CQC rank; In files: stats.py:142; */
"Helpless" = "Безпорадний";
/* stats.py: CQC rank; In files: stats.py:145; */
/* stats.py: CQC rank; In files: stats.py:143; */
"Mostly Helpless" = "Здебільшого Безпомічний";
/* stats.py: CQC rank; In files: stats.py:146; */
/* stats.py: CQC rank; In files: stats.py:144; */
"Amateur" = "Аматор";
/* stats.py: CQC rank; In files: stats.py:147; */
/* stats.py: CQC rank; In files: stats.py:145; */
"Semi Professional" = "Напів-професійний";
/* stats.py: CQC rank; In files: stats.py:148; */
/* stats.py: CQC rank; In files: stats.py:146; */
"Professional" = "Професійний";
/* stats.py: CQC rank; In files: stats.py:149; */
/* stats.py: CQC rank; In files: stats.py:147; */
"Champion" = "Чемпіон";
/* stats.py: CQC rank; In files: stats.py:150; */
/* stats.py: CQC rank; In files: stats.py:148; */
"Hero" = "Герой";
/* stats.py: CQC rank; In files: stats.py:151; */
/* stats.py: CQC rank; In files: stats.py:149; */
"Gladiator" = "Гладіатор";
/* stats.py: Federation rank; In files: stats.py:157; */
/* stats.py: Federation rank; In files: stats.py:155; */
"Recruit" = "Рекрут";
/* stats.py: Federation rank; In files: stats.py:158; */
/* stats.py: Federation rank; In files: stats.py:156; */
"Cadet" = "Кадет";
/* stats.py: Federation rank; In files: stats.py:159; */
/* stats.py: Federation rank; In files: stats.py:157; */
"Midshipman" = "Гардемарин";
/* stats.py: Federation rank; In files: stats.py:160; */
/* stats.py: Federation rank; In files: stats.py:158; */
"Petty Officer" = "Унтер Офіцер";
/* stats.py: Federation rank; In files: stats.py:161; */
/* stats.py: Federation rank; In files: stats.py:159; */
"Chief Petty Officer" = "Старший Унтер Офіцер";
/* stats.py: Federation rank; In files: stats.py:162; */
/* stats.py: Federation rank; In files: stats.py:160; */
"Warrant Officer" = "Старший сержант";
/* stats.py: Federation rank; In files: stats.py:163; */
/* stats.py: Federation rank; In files: stats.py:161; */
"Ensign" = "Прапорщик";
/* stats.py: Federation rank; In files: stats.py:164; */
/* stats.py: Federation rank; In files: stats.py:162; */
"Lieutenant" = "Лейтенант";
/* stats.py: Federation rank; In files: stats.py:165; */
/* stats.py: Federation rank; In files: stats.py:163; */
"Lieutenant Commander" = "Капітан III рангу";
/* stats.py: Federation rank; In files: stats.py:166; */
/* stats.py: Federation rank; In files: stats.py:164; */
"Post Commander" = "Капітан II рангу";
/* stats.py: Federation rank; In files: stats.py:167; */
/* stats.py: Federation rank; In files: stats.py:165; */
"Post Captain" = "Капітан I рангу";
/* stats.py: Federation rank; In files: stats.py:168; */
/* stats.py: Federation rank; In files: stats.py:166; */
"Rear Admiral" = "Контр-адмірал";
/* stats.py: Federation rank; In files: stats.py:169; */
/* stats.py: Federation rank; In files: stats.py:167; */
"Vice Admiral" = "Віце-адмірал";
/* stats.py: Federation rank; In files: stats.py:170; */
/* stats.py: Federation rank; In files: stats.py:168; */
"Admiral" = "Адмірал";
/* stats.py: Empire rank; In files: stats.py:176; */
/* stats.py: Empire rank; In files: stats.py:174; */
"Outsider" = "Чужинець";
/* stats.py: Empire rank; In files: stats.py:177; */
/* stats.py: Empire rank; In files: stats.py:175; */
"Serf" = "Кріпак";
/* stats.py: Empire rank; In files: stats.py:179; */
/* stats.py: Empire rank; In files: stats.py:177; */
"Squire" = "Зброєносець";
/* stats.py: Empire rank; In files: stats.py:180; */
/* stats.py: Empire rank; In files: stats.py:178; */
"Knight" = "Лицар";
/* stats.py: Empire rank; In files: stats.py:181; */
/* stats.py: Empire rank; In files: stats.py:179; */
"Lord" = "Пан";
/* stats.py: Empire rank; In files: stats.py:182; */
/* stats.py: Empire rank; In files: stats.py:180; */
"Baron" = "Барон";
/* stats.py: Empire rank; In files: stats.py:183; */
/* stats.py: Empire rank; In files: stats.py:181; */
"Viscount" = "Віконт";
/* stats.py: Empire rank; In files: stats.py:184; */
/* stats.py: Empire rank; In files: stats.py:182; */
"Count" = "Граф";
/* stats.py: Empire rank; In files: stats.py:185; */
/* stats.py: Empire rank; In files: stats.py:183; */
"Earl" = "Дворянин";
/* stats.py: Empire rank; In files: stats.py:186; */
/* stats.py: Empire rank; In files: stats.py:184; */
"Marquis" = "Маркіз";
/* stats.py: Empire rank; In files: stats.py:187; */
/* stats.py: Empire rank; In files: stats.py:185; */
"Duke" = "Герцог";
/* stats.py: Empire rank; In files: stats.py:188; */
/* stats.py: Empire rank; In files: stats.py:186; */
"Prince" = "Принц";
/* stats.py: Empire rank; In files: stats.py:189; */
/* stats.py: Empire rank; In files: stats.py:187; */
"King" = "Цар";
/* stats.py: Power rank; In files: stats.py:195; */
/* stats.py: Power rank; In files: stats.py:193; */
"Rating 1" = "Рейтинг 1";
/* stats.py: Power rank; In files: stats.py:196; */
/* stats.py: Power rank; In files: stats.py:194; */
"Rating 2" = "Рейтинг 2";
/* stats.py: Power rank; In files: stats.py:197; */
/* stats.py: Power rank; In files: stats.py:195; */
"Rating 3" = "Рейтинг 3";
/* stats.py: Power rank; In files: stats.py:198; */
/* stats.py: Power rank; In files: stats.py:196; */
"Rating 4" = "Рейтинг 4";
/* stats.py: Power rank; In files: stats.py:199; */
/* stats.py: Power rank; In files: stats.py:197; */
"Rating 5" = "Рейтинг 5";
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:413; */
/* stats.py: Status dialog subtitle - CR value of ship; In files: stats.py:409; */
"Value" = "Вартість";
/* stats.py: Status dialog title; In files: stats.py:422; */
/* stats.py: Status dialog title; In files: stats.py:418; */
"Ships" = "Кораблі";
+420 -426
View File
File diff suppressed because it is too large. Load diff
-1
View File
@@ -337,4 +337,3 @@ proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
+157 -72
View File
@@ -99,8 +99,8 @@ liable to change without notice.
`from prefs import prefsVersion` - to allow for versioned preferences.
`from companion import CAPIData, SERVER_LIVE, SERVER_LEGACY, SERVER_BETA` -
`CAPIData` is the actual type of `data` as passed into `cmdr_data()` and
`cmdr_data_legacy()`.
`CAPIData` is the actual type of `data` as passed into `cmdr_data()`,
`cmdr_data_legacy()` and `capi_fleetcarrier()`.
See [Commander Data from Frontier CAPI](#commander-data-from-frontier-capi))
for further information.
@@ -561,9 +561,9 @@ See [Avoiding potential pitfalls](#avoiding-potential-pitfalls).
### Events
Once you have created your plugin and EDMarketConnector has loaded it there
are four other functions you can define to be notified by EDMarketConnector
are five other functions you can define to be notified by EDMarketConnector
when something happens: `journal_entry()`, `journal_entry_cqc()`,
`dashboard_entry()` and `cmdr_data()`.
`dashboard_entry()`, `cmdr_data()` and `capi_fleetcarrier()`.
Your events all get called on the main Tkinter loop so be sure not to block for
very long or the app will appear to freeze. If you have a long running
@@ -606,55 +606,63 @@ This gets called when EDMarketConnector sees a new entry in the game's journal.
Content of `state` (updated to the current journal entry):
| Field | Type | Description |
| :------------------- | :-------------------------: |:----------------------------------------------------------------------------------------------------------------|
| `GameLanguage` | `Optional[str]` | `language` value from `Fileheader` event. |
| `GameVersion` | `Optional[str]` | `version` value from `Fileheader` event. |
| `GameBuild` | `Optional[str]` | `build` value from `Fileheader` event. |
| `Captain` | `Optional[str]` | Name of the commander who's crew you're on, if any |
| `Cargo` | `dict` | Current cargo. Note that this will be totals, and any mission specific duplicates will be counted together |
| `CargoJSON` | `dict` | content of cargo.json as of last read. |
| `Credits` | `int` | Current credits balance |
| `FID` | `str` | Frontier commander ID |
| `Horizons` | `bool` | From `LoadGame` event. |
| `Odyssey` | `bool` | From `LoadGame` event. `False` if not present, else the event value. |
| `Loan` | `Optional[int]` | Current loan amount, if any |
| `Raw` | `dict` | Current raw engineering materials |
| `Manufactured` | `dict` | Current manufactured engineering materials |
| `Encoded` | `dict` | Current encoded engineering materials |
| `Component` | `dict` | Current component materials |
| `Engineers` | `dict` | Current Raw engineering materials |
| `Rank` | `Dict[str, Tuple[int, int]` | Current ranks, each entry is a tuple of the current rank, and age |
| `Statistics` | `dict` | Contents of a Journal Statistics event, ie, data shown in the stats panel. See the Journal manual for more info |
| `Role` | `Optional[str]` | Current role if in multi-crew, one of `Idle`, `FireCon`, `FighterCon` |
| `Friends` | `set` | Currently online friend |
| `ShipID` | `int` | Frontier ID of current ship |
| `ShipIdent` | `str` | Current user-set ship ID |
| `ShipName` | `str` | Current user-set ship name |
| `ShipType` | `str` | Internal name for the current ship type |
| `HullValue` | `int` | Current ship value, excluding modules |
| `ModulesValue` | `int` | Value of the current ship's modules |
| `Rebuy` | `int` | Current ship's rebuy cost |
| `Modules` | `dict` | Currently fitted modules |
| `NavRoute` | `dict` | Last plotted multi-hop route[1] |
| `ModuleInfo` | `dict` | Last loaded ModulesInfo.json data |
| `IsDocked` | `bool` | Whether the Cmdr is currently docked *in their own ship*. |
| `OnFoot` | `bool` | Whether the Cmdr is on foot |
| `Component` | `dict` | 'Component' MicroResources in Odyssey, `int` count each. |
| `Item` | `dict` | 'Item' MicroResources in Odyssey, `int` count each. |
| `Consumable` | `dict` | 'Consumable' MicroResources in Odyssey, `int` count each. |
| `Data` | `dict` | 'Data' MicroResources in Odyssey, `int` count each. |
| `BackPack` | `dict` | `dict` of Odyssey MicroResources in backpack. |
| `BackpackJSON` | `dict` | Content of Backpack.json as of last read. |
| `ShipLockerJSON` | `dict` | Content of ShipLocker.json as of last read. |
| `SuitCurrent` | `dict` | CAPI-returned data of currently worn suit. NB: May be `None` if no data. |
| `Suits` | `dict`[2] | CAPI-returned data of owned suits. NB: May be `None` if no data. |
| `SuitLoadoutCurrent` | `dict` | CAPI-returned data of current Suit Loadout. NB: May be `None` if no data. |
| `SuitLoadouts` | `dict`[2] | CAPI-returned data of all Suit Loadouts. NB: May be `None` if no data. |
| `Taxi` | `Optional[bool]` | Whether or not we're currently in a taxi. NB: This is best effort with what the journals provide. |
| `Dropship` | `Optional[bool]` | Whether or not the above taxi is a Dropship |
| `Body` | `Optional[str]` | The body we're currently on / in the SOI of |
| `BodyType` | `Optional[str]` | The type of body that `Body` refers to |
| Field | Type | Description |
|:----------------------|:---------------------------:|:----------------------------------------------------------------------------------------------------------------|
| `GameLanguage` | `Optional[str]` | `language` value from `Fileheader` event. |
| `GameVersion` | `Optional[str]` | `version` value from `Fileheader` event. |
| `GameBuild` | `Optional[str]` | `build` value from `Fileheader` event. |
| `Captain`[3] | `Optional[str]` | Name of the commander who's crew you're on, if any |
| `Cargo` | `dict` | Current cargo. Note that this will be totals, and any mission specific duplicates will be counted together |
| `CargoJSON` | `dict` | content of cargo.json as of last read. |
| `Credits` | `int` | Current credits balance |
| `FID` | `str` | Frontier commander ID |
| `Horizons` | `bool` | From `LoadGame` event. |
| `Odyssey` | `bool` | From `LoadGame` event. `False` if not present, else the event value. |
| `Loan` | `Optional[int]` | Current loan amount, if any |
| `Raw` | `dict` | Current raw engineering materials |
| `Manufactured` | `dict` | Current manufactured engineering materials |
| `Encoded` | `dict` | Current encoded engineering materials |
| `Component` | `dict` | Current component materials |
| `Engineers` | `dict` | Current Raw engineering materials |
| `Rank` | `Dict[str, Tuple[int, int]` | Current ranks, each entry is a tuple of the current rank, and age |
| `Statistics` | `dict` | Contents of a Journal Statistics event, ie, data shown in the stats panel. See the Journal manual for more info |
| `Role` | `Optional[str]` | Current role if in multi-crew, one of `Idle`, `FireCon`, `FighterCon` |
| `Friends` | `set` | Currently online friend |
| `ShipID` | `int` | Frontier ID of current ship |
| `ShipIdent` | `str` | Current user-set ship ID |
| `ShipName` | `str` | Current user-set ship name |
| `ShipType` | `str` | Internal name for the current ship type |
| `HullValue` | `int` | Current ship value, excluding modules |
| `ModulesValue` | `int` | Value of the current ship's modules |
| `Rebuy` | `int` | Current ship's rebuy cost |
| `Modules` | `dict` | Currently fitted modules |
| `NavRoute` | `dict` | Last plotted multi-hop route[1] |
| `ModuleInfo` | `dict` | Last loaded ModulesInfo.json data |
| `IsDocked` | `bool` | Whether the Cmdr is currently docked *in their own ship*. |
| `OnFoot`[3] | `bool` | Whether the Cmdr is on foot |
| `Component` | `dict` | 'Component' MicroResources in Odyssey, `int` count each. |
| `Item` | `dict` | 'Item' MicroResources in Odyssey, `int` count each. |
| `Consumable` | `dict` | 'Consumable' MicroResources in Odyssey, `int` count each. |
| `Data` | `dict` | 'Data' MicroResources in Odyssey, `int` count each. |
| `BackPack` | `dict` | `dict` of Odyssey MicroResources in backpack. |
| `BackpackJSON` | `dict` | Content of Backpack.json as of last read. |
| `ShipLockerJSON` | `dict` | Content of ShipLocker.json as of last read. |
| `SuitCurrent` | `dict` | CAPI-returned data of currently worn suit. NB: May be `None` if no data. |
| `Suits` | `dict`[2] | CAPI-returned data of owned suits. NB: May be `None` if no data. |
| `SuitLoadoutCurrent` | `dict` | CAPI-returned data of current Suit Loadout. NB: May be `None` if no data. |
| `SuitLoadouts` | `dict`[2] | CAPI-returned data of all Suit Loadouts. NB: May be `None` if no data. |
| `Taxi` | `Optional[bool]` | Whether or not we're currently in a taxi. NB: This is best effort with what the journals provide. |
| `Dropship` | `Optional[bool]` | Whether or not the above taxi is a Dropship |
| `SystemAddress`[3] | `Optional[int]` | Unique [ID64](http://disc.thargoid.space/ID64) of the star system we're currently in |
| `SystemName`[3] | `Optional[str]` | Name of the star system we're currently in |
| `SystemPopulation`[3] | `Optional[int]` | Population of the star system we're currently in |
| `StarPos`[3] | `Optional[tuple[float]]` | Galaxy co-ordinates of the system we're currently in |
| `Body`[3][4] | `Optional[str]` | Name of the body we're currently on / in the SOI of |
| `BodyID`[3][4] | `Optional[int]` | ID of the body we're currently on / in the SOI of |
| `BodyType`[3][4] | `Optional[str]` | The type of body that `Body` refers to |
| `StationName`[3] | `Optional[str]` | Name of the station we're docked at, if applicable |
| `MarketID`[3] | `Optional[str]` | MarketID of the station we're docked at, if applicable |
| `StationType`[3] | `Optional[str]` | Type of the station we're docked at, if applicable |
[1] - Contents of `NavRoute` not changed if a `NavRouteClear` event is seen,
but plugins will see the `NavRouteClear` event.
@@ -676,6 +684,31 @@ least one member is missing, so the indices are not contiguous). We choose to
always convert to the integer-keyed `dict` form so that code utilising the data
is simpler.
[3] - Forced to `None` if the player joins another player's ship in remote
multi-crew.
[4] - There are some caveats with the Body data. Firstly the name and ID
can be for the orbital station or fleet carrier the player is docked at.
Check 'BodyType' before using the values.
Secondly there is an issue with close-orbiting binary bodies. If the player:
1. Enters Orbital Cruise around a Body an 'ApproachBody' event is emitted
and the tracking will update to reflect this.
2. If the player then flies *in Orbital Cruise without entering Supercruise
proper* to the close-orbiting binary partner of the Body then *there is no
new 'ApproachBody' event to indicate the new Body's details*. **Thus this
tracking will incorrectly indicate the first Body still**.
So, before making use of any of this Body state a plugin should:
1. Have a `dashboard_entry()` method and track the Body name present in its
data.
2. Cross-check that Body name with `state['Body']` before making use of any
of `state'`s Body data.
See `plugins/eddn.py` for an example of this in `export_journal_codexentry()`.
New in version 4.1.6:
`CargoJSON` contains the raw data from the last read of `Cargo.json` passed
@@ -765,13 +798,26 @@ re-start EDMarketConnector. That will be present when plugins are invoked
with the synthetic `StartUp` event. NB: Might just be a `NavRouteClear` event
if that's what was in the file.
New in version 5.8.0:
`StarPos`, `SystemAddress`, `SystemName` and `SystemPopulation` have been
added to the `state` dictionary. Best efforts data pertaining to the star
system the player is in.
`BodyID` and `BodyType` have been added to the `state` dictionary. These
now track in the same manner as prior core EDDN plugin code. Check the
documentation above for some caveats. Do not just blindly use this data, or
the 'Body' name value.
`StationName`, `MarketID`, and `StationType` added to the `state` dictionary.
___
##### Synthetic Events
A special "StartUp" entry is sent if EDMarketConnector is started while the
game is already running. In this case you won't receive initial events such as
"LoadGame", "Rank", "Location", etc. However the `state` dictionary will
"LoadGame", "Rank", "Location", etc. However, the `state` dictionary will
reflect the cumulative effect of these missed events.
**NB: Any of the values in this might be `None` if the Cmdr has loaded into
@@ -908,12 +954,11 @@ constants.
---
### Commander Data from Frontier CAPI
If a plugin has a `cmdr_data()` function it gets called when the application
has just fetched fresh Cmdr and station data from Frontier's servers, **but not
for the Legacy galaxy**. See `cmdr_data_legacy()` below for Legacy data
handling.
### Data from Frontier CAPI
#### Commander, Market and Shipyard Data
If a plugin has a `cmdr_data()` function it gets called when the application has just fetched fresh CMDR, station and shipyard data from Frontier's CAPI servers, **but not for the Legacy galaxy**. See `cmdr_data_legacy()` below for Legacy data handling.
```python
from companion import CAPIData, SERVER_LIVE, SERVER_LEGACY, SERVER_BETA
@@ -941,19 +986,59 @@ def cmdr_data(data, is_beta):
| :-------- | :--------------: | :------------------------------------------------------------------------------------------------------- |
| `data` | `CAPIData` | `/profile` API response, with `/market` and `/shipyard` added under the keys `marketdata` and `shipdata` |
| `is_beta` | `bool` | If the game is currently in beta |
`CAPIData` is a class, which you can `from companion import CAPIDATA`, and is
based on `UserDict`. The actual data from CAPI queries is thus accessible
via python's normal `data['key']` syntax. However, being a class, it can also
have extra properties, such as `source_host`, as shown above. Plugin authors
are free to use *that* property, **but MUST NOT rely on any other extra
properties present in `CAPIData`, they are for internal use only.**
#### Fleet Carrier Data
If a plugin has a `capi_fleetcarrier()` function it gets called when the application has just fetched fresh Fleetcarrier data from Frontier's CAPI servers. This is done when `CarrierBuy`or `CarrierStats` events are detected in the Player Journal. To avoid flooding Frontier's CAPI server, a throttle is applied to ensure a significant interval between requests (currently 15 mins). Also be aware that calls to the `/fleetcarrier` CAPI endpoint have been reported to take a very long time to return, potentially up to 20 minutes. Delays in responses from this endpoint could delay other CAPI queries.
```python
from companion import CAPIData, SERVER_LIVE, SERVER_LEGACY, SERVER_BETA
def capi_fleetcarrier(data):
"""
We have new data on our Fleet Carrier
"""
if data.get('name') is None or data['name'].get('callsign') is None:
raise ValueError("this isn't possible")
logger.info(data['name']['callsign'])
# Determining source galaxy for the data
if data.source_host == SERVER_LIVE:
...
elif data.source_host == SERVER_BETA:
...
elif data.source_host == SERVER_LEGACY:
...
```
| Parameter | Type | Description |
| :-------- | :--------------: | :------------------------------------------------------------------------------------------------------- |
| `data` | `CAPIData` | `/fleetcarrier` API response |
#### CAPIData and Available Properties
`CAPIData` is a class, which you can `from companion import CAPIDATA`, and is based on `UserDict`. The actual data from CAPI queries is thus accessible via python's normal `data['key']` syntax. However, being a class, it can also have extra properties, such as `source_host`, as shown in the code example above.
Plugin authors are free to use the following properties of `CAPIData`, **but MUST NOT rely on any other extra properties, they are for internal use only.**
| Property | Type | Description |
| :------------- | :--------------: | :------------------------------------------------------------------------------------------------------- |
| `data` | `Dict` | The data returned by the CAPI query. For the `cmdr_data()` callback, if the player is docked at a station, and the relevant services are available then the `lastStarport` key's value will have been augmented with `/market` and/or `/shipyard` data. **Do not assume this will always be the case**. |
| `source_host` | `str` | `SERVER_LIVE` \| `SERVER_BETA` \| `SERVER_LEGACY` the current galaxy mode. |
| `request_cmdr` | `str` | The name of the active CMDR _at the point the request was made_. In the case of a CAPI request taking a long time to return, the user may have switched CMDR during the request, so this may be different to the current CMDR. |
See [this documentation](https://github.com/Athanasius/fd-api/blob/main/docs/FrontierDevelopments-CAPI-endpoints.md) for details of the expected content structure and data for CAPI queries.
If there is a killswitch in effect for some of the CAPI endpoints, then the
data passed to this function might not be as complete as you expect. Code
defensively.
#### CAPI data for Legacy
When CAPI data has been retrieved from the separate CAPI host for the Legacy
galaxy, because the Journal gameversion indicated the player is playing/last
played in that galaxy, a different function will be called,
`cmdr_data_legacy()`.
When CAPI data has been retrieved from the separate CAPI host for the Legacy galaxy, because the Journal gameversion indicated the player is playing / last played in that galaxy, a different function will be called, `cmdr_data_legacy()`. Note that there is no legacy equivalent to `capi_fleetcarrier()`, so always use the `source_host` property to determine the user's galaxy.
```python
def cmdr_data_legacy(data, is_beta):
@@ -1073,7 +1158,7 @@ time after the corresponding `journal_entry()` event.
## Error messages
You can display an error in EDMarketConnector's status area by returning a
string from your `journal_entry()`, `dashboard_entry()` or `cmdr_data()`
string from your `journal_entry()`, `dashboard_entry()`, `cmdr_data()` or `capi_fleetcarrier()`
function, or asynchronously (e.g. from a "worker" thread that is performing a
long-running operation) by calling `plug.show_error()`. Either method will
cause the "bad" sound to be played (unless the user has muted sound).
@@ -1099,7 +1184,7 @@ _ = functools.partial(l10n.Translations.translate, context=__file__)
Wrap each string that needs translating with the `_()` function, e.g.:
```python
status["text"] = _('Happy!') # Main window status
somewidget["text"] = _("Happy!")
```
If you display localized strings in EDMarketConnector's main window you should
@@ -1302,7 +1387,7 @@ versions of EDMarketConnector:
`Settings` > `Plugins` tab.
- Check that callback functions `plugin_prefs`, `prefs_changed`,
`journal_entry`, `dashboard_entry` and `cmdr_data`, if used, are declared
`journal_entry`, `dashboard_entry`, `cmdr_data` and `capi_fleetcarrier`, if used, are declared
with the correct number of arguments. Older versions of this app were
tolerant of missing arguments in these function declarations.
+1 -1
View File
@@ -100,7 +100,7 @@ def addmodules(data): # noqa: C901, CCR001
if not data['lastStarport'].get('modules'):
return
outfile = 'outfitting.csv'
outfile = pathlib.Path('outfitting.csv')
modules = {}
fields = ('id', 'symbol', 'category', 'name', 'mount', 'guidance', 'ship', 'class', 'rating', 'entitlement')
+123 -33
View File
@@ -28,6 +28,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional, OrderedDic
import requests
import config as conf_module
import killswitch
import protocol
from config import config, user_agent
from edmc_data import companion_category_map as category_map
@@ -45,7 +46,9 @@ else:
capi_query_cooldown = 60 # Minimum time between (sets of) CAPI queries
capi_fleetcarrier_query_cooldown = 60 * 15 # Minimum time between CAPI fleetcarrier queries
capi_default_requests_timeout = 10
capi_fleetcarrier_requests_timeout = 60
auth_timeout = 30 # timeout for initial auth
# Used by both class Auth and Session
@@ -65,7 +68,8 @@ class CAPIData(UserDict):
self,
data: Union[str, Dict[str, Any], 'CAPIData', None] = None,
source_host: Optional[str] = None,
source_endpoint: Optional[str] = None
source_endpoint: Optional[str] = None,
request_cmdr: Optional[str] = None
) -> None:
if data is None:
super().__init__()
@@ -80,6 +84,7 @@ class CAPIData(UserDict):
self.source_host = source_host
self.source_endpoint = source_endpoint
self.request_cmdr = request_cmdr
if source_endpoint is None:
return
@@ -325,6 +330,14 @@ class Auth(object):
"""
logger.debug(f'Trying for "{self.cmdr}"')
should_return: bool
new_data: dict[str, Any]
should_return, new_data = killswitch.check_killswitch('capi.auth', {})
if should_return:
logger.warning('capi.auth has been disabled via killswitch. Returning.')
return None
self.verifier = None
cmdrs = config.get_list('cmdrs', default=[])
logger.debug(f'Cmdrs: {cmdrs}')
@@ -393,9 +406,11 @@ class Auth(object):
return None
def authorize(self, payload: str) -> str: # noqa: CCR001
"""Handle oAuth authorization callback.
"""
Handle oAuth authorization callback.
:return: access token if successful, otherwise raises CredentialsError.
:return: access token if successful
:raises CredentialsError
"""
logger.debug('Checking oAuth authorization callback')
if '?' not in payload:
@@ -593,7 +608,7 @@ class EDMCCAPIFailedRequest(EDMCCAPIReturn):
):
super().__init__(query_time=query_time, play_sound=play_sound, auto_update=auto_update)
self.message: str = message # User-friendly reason for failure.
self.exception: int = exception # Exception that recipient should raise.
self.exception: Exception = exception # Exception that recipient should raise.
class Session(object):
@@ -604,6 +619,8 @@ class Session(object):
FRONTIER_CAPI_PATH_PROFILE = '/profile'
FRONTIER_CAPI_PATH_MARKET = '/market'
FRONTIER_CAPI_PATH_SHIPYARD = '/shipyard'
FRONTIER_CAPI_PATH_FLEETCARRIER = '/fleetcarrier'
# This is a dummy value, to signal to Session.capi_query_worker that we
# the 'station' triplet of queries.
_CAPI_PATH_STATION = '_edmc_station'
@@ -611,7 +628,7 @@ class Session(object):
def __init__(self) -> None:
self.state = Session.STATE_INIT
self.credentials: Optional[Dict[str, Any]] = None
self.requests_session: Optional[requests.Session] = None
self.requests_session = requests.Session()
self.auth: Optional[Auth] = None
self.retrying = False # Avoid infinite loop when successful auth / unsuccessful query
self.tk_master: Optional[tk.Tk] = None
@@ -644,9 +661,9 @@ class Session(object):
def start_frontier_auth(self, access_token: str) -> None:
"""Start an oAuth2 session."""
logger.debug('Starting session')
self.requests_session = requests.Session()
self.requests_session.headers['Authorization'] = f'Bearer {access_token}'
self.requests_session.headers['User-Agent'] = user_agent
self.state = Session.STATE_OK
def login(self, cmdr: Optional[str] = None, is_beta: Optional[bool] = None) -> bool:
@@ -655,6 +672,14 @@ class Session(object):
:return: True if login succeeded, False if re-authorization initiated.
"""
should_return: bool
new_data: dict[str, Any]
should_return, new_data = killswitch.check_killswitch('capi.auth', {})
if should_return:
logger.warning('capi.auth has been disabled via killswitch. Returning.')
return False
if not Auth.CLIENT_ID:
logger.error('self.CLIENT_ID is None')
raise CredentialsError('cannot login without a valid Client ID')
@@ -680,7 +705,7 @@ class Session(object):
else:
logger.debug('changed account or retrying login during auth')
self.close()
self.reinit_session()
self.credentials = credentials
self.state = Session.STATE_INIT
@@ -720,22 +745,30 @@ class Session(object):
raise # Bad thing happened
def close(self) -> None:
"""Close Frontier authorization session."""
"""Close the `request.Session()."""
try:
self.requests_session.close()
except Exception as e:
logger.debug('Frontier Auth: closing', exc_info=e)
def reinit_session(self, reopen: bool = True) -> None:
"""
Re-initialise the session's `request.Session()`.
:param reopen: Whether to open a new session.
"""
self.state = Session.STATE_INIT
if self.requests_session:
try:
self.requests_session.close()
self.close()
except Exception as e:
logger.debug('Frontier Auth: closing', exc_info=e)
self.requests_session = None
if reopen:
self.requests_session = requests.Session()
def invalidate(self) -> None:
"""Invalidate Frontier authorization credentials."""
logger.debug('Forcing a full re-authentication')
# Force a full re-authentication
self.close()
self.reinit_session()
Auth.invalidate(self.credentials['cmdr']) # type: ignore
######################################################################
@@ -759,9 +792,17 @@ class Session(object):
:param timeout: requests query timeout to use.
:return: The resulting CAPI data, of type CAPIData.
"""
capi_data: CAPIData
capi_data: CAPIData = CAPIData()
should_return: bool
new_data: dict[str, Any]
should_return, new_data = killswitch.check_killswitch('capi.request.' + capi_endpoint, {})
if should_return:
logger.warning(f"capi.request.{capi_endpoint} has been disabled by killswitch. Returning.")
return capi_data
try:
logger.trace_if('capi.worker', 'Sending HTTP request...')
logger.trace_if('capi.worker', f'Sending HTTP request for {capi_endpoint} ...')
if conf_module.capi_pretend_down:
raise ServerConnectionError(f'Pretending CAPI down: {capi_endpoint}')
@@ -778,7 +819,7 @@ class Session(object):
# r.status_code = 401
# raise requests.HTTPError
capi_json = r.json()
capi_data = CAPIData(capi_json, capi_host, capi_endpoint)
capi_data = CAPIData(capi_json, capi_host, capi_endpoint, monitor.cmdr)
self.capi_raw_data.record_endpoint(
capi_endpoint, r.content.decode(encoding='utf-8'),
datetime.datetime.utcnow()
@@ -841,6 +882,10 @@ class Session(object):
"""
station_data = capi_single_query(capi_host, self.FRONTIER_CAPI_PATH_PROFILE, timeout=timeout)
if not station_data.get('commander'):
# If even this doesn't exist, probably killswitched.
return station_data
if not station_data['commander'].get('docked') and not monitor.state['OnFoot']:
return station_data
@@ -881,6 +926,10 @@ class Session(object):
if services.get('commodities'):
market_data = capi_single_query(capi_host, self.FRONTIER_CAPI_PATH_MARKET, timeout=timeout)
if not market_data.get('id'):
# Probably killswitched
return station_data
if last_starport_id != int(market_data['id']):
logger.warning(f"{last_starport_id!r} != {int(market_data['id'])!r}")
raise ServerLagging()
@@ -891,6 +940,10 @@ class Session(object):
if services.get('outfitting') or services.get('shipyard'):
shipyard_data = capi_single_query(capi_host, self.FRONTIER_CAPI_PATH_SHIPYARD, timeout=timeout)
if not shipyard_data.get('id'):
# Probably killswitched
return station_data
if last_starport_id != int(shipyard_data['id']):
logger.warning(f"{last_starport_id!r} != {int(shipyard_data['id'])!r}")
raise ServerLagging()
@@ -918,6 +971,10 @@ class Session(object):
if query.endpoint == self._CAPI_PATH_STATION:
capi_data = capi_station_queries(query.capi_host)
elif query.endpoint == self.FRONTIER_CAPI_PATH_FLEETCARRIER:
capi_data = capi_single_query(query.capi_host, self.FRONTIER_CAPI_PATH_FLEETCARRIER,
timeout=capi_fleetcarrier_requests_timeout)
else:
capi_data = capi_single_query(query.capi_host, self.FRONTIER_CAPI_PATH_PROFILE)
@@ -946,7 +1003,8 @@ class Session(object):
# event too, so assume it will be polling the response queue.
if query.tk_response_event is not None:
logger.trace_if('capi.worker', 'Sending <<CAPIResponse>>')
self.tk_master.event_generate('<<CAPIResponse>>')
if self.tk_master is not None:
self.tk_master.event_generate('<<CAPIResponse>>')
logger.info('CAPI worker thread DONE')
@@ -988,6 +1046,35 @@ class Session(object):
auto_update=auto_update
)
)
def fleetcarrier(
self, query_time: int, tk_response_event: str | None = None,
play_sound: bool = False, auto_update: bool = False
) -> None:
"""
Perform CAPI query for fleetcarrier data.
:param query_time: When this query was initiated.
:param tk_response_event: Name of tk event to generate when response queued.
:param play_sound: Whether the app should play a sound on error.
:param auto_update: Whether this request was triggered automatically.
"""
capi_host = self.capi_host_for_galaxy()
if not capi_host:
return
# Ask the thread worker to perform a fleetcarrier query
logger.trace_if('capi.worker', 'Enqueueing fleetcarrier request')
self.capi_request_queue.put(
EDMCCAPIRequest(
capi_host=capi_host,
endpoint=self.FRONTIER_CAPI_PATH_FLEETCARRIER,
tk_response_event=tk_response_event,
query_time=query_time,
play_sound=play_sound,
auto_update=auto_update
)
)
######################################################################
######################################################################
@@ -1038,24 +1125,27 @@ class Session(object):
def dump_capi_data(self, data: CAPIData) -> None:
"""Dump CAPI data to file for examination."""
if os.path.isdir('dump'):
try:
system = data['lastSystem']['name']
file_name: str = ""
if data.source_endpoint == self.FRONTIER_CAPI_PATH_FLEETCARRIER:
file_name += f"FleetCarrier.{data['name']['callsign']}"
except (KeyError, ValueError):
system = '<unknown system>'
else:
try:
file_name += data['lastSystem']['name']
try:
if data['commander'].get('docked'):
station = f'.{data["lastStarport"]["name"]}'
except (KeyError, ValueError):
file_name += 'unknown system'
else:
station = ''
try:
if data['commander'].get('docked'):
file_name += f'.{data["lastStarport"]["name"]}'
except (KeyError, ValueError):
station = '<unknown station>'
except (KeyError, ValueError):
file_name += '.unknown station'
timestamp = time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime())
with open(f'dump/{system}{station}.{timestamp}.json', 'wb') as h:
file_name += time.strftime('.%Y-%m-%dT%H.%M.%S', time.localtime())
file_name += '.json'
with open(f'dump/{file_name}', 'wb') as h:
h.write(json.dumps(data, cls=CAPIDataEncoder,
ensure_ascii=False,
indent=2,
+14 -11
View File
@@ -40,7 +40,7 @@ import sys
import traceback
import warnings
from abc import abstractmethod
from typing import Any, Callable, List, Optional, Type, TypeVar, Union
from typing import Any, Callable, Optional, Type, TypeVar
import semantic_version
@@ -52,17 +52,17 @@ appcmdname = 'EDMC'
# <https://semver.org/#semantic-versioning-specification-semver>
# Major.Minor.Patch(-prerelease)(+buildmetadata)
# NB: Do *not* import this, use the functions appversion() and appversion_nobuild()
_static_appversion = '5.7.0'
_static_appversion = '5.8.0'
_cached_version: Optional[semantic_version.Version] = None
copyright = '© 2015-2019 Jonathan Harris, 2020-2022 EDCD'
copyright = '© 2015-2019 Jonathan Harris, 2020-2023 EDCD'
update_feed = 'https://raw.githubusercontent.com/EDCD/EDMarketConnector/releases/edmarketconnector.xml'
update_interval = 8*60*60
# Providers marked to be in debug mode. Generally this is expected to switch to sending data to a log file
debug_senders: List[str] = []
debug_senders: list[str] = []
# TRACE logging code that should actually be used. Means not spamming it
# *all* if only interested in some things.
trace_on: List[str] = []
trace_on: list[str] = []
capi_pretend_down: bool = False
capi_debug_access_token: Optional[str] = None
@@ -293,7 +293,7 @@ class AbstractConfig(abc.ABC):
@staticmethod
def _suppress_call(
func: Callable[..., _T], exceptions: Union[Type[BaseException], List[Type[BaseException]]] = Exception,
func: Callable[..., _T], exceptions: Type[BaseException] | list[Type[BaseException]] = Exception,
*args: Any, **kwargs: Any
) -> Optional[_T]:
if exceptions is None:
@@ -307,7 +307,10 @@ class AbstractConfig(abc.ABC):
return None
def get(self, key: str, default: Union[list, str, bool, int] = None) -> Union[list, str, bool, int]:
def get(
self, key: str,
default: list | str | bool | int | None = None
) -> list | str | bool | int | None:
"""
Return the data for the requested key, or a default.
@@ -334,7 +337,7 @@ class AbstractConfig(abc.ABC):
return default # type: ignore
@abstractmethod
def get_list(self, key: str, *, default: list = None) -> list:
def get_list(self, key: str, *, default: list | None = None) -> list:
"""
Return the list referred to by the given key if it exists, or the default.
@@ -343,7 +346,7 @@ class AbstractConfig(abc.ABC):
raise NotImplementedError
@abstractmethod
def get_str(self, key: str, *, default: str = None) -> str:
def get_str(self, key: str, *, default: str | None = None) -> str:
"""
Return the string referred to by the given key if it exists, or the default.
@@ -356,7 +359,7 @@ class AbstractConfig(abc.ABC):
raise NotImplementedError
@abstractmethod
def get_bool(self, key: str, *, default: bool = None) -> bool:
def get_bool(self, key: str, *, default: bool | None = None) -> bool:
"""
Return the bool referred to by the given key if it exists, or the default.
@@ -396,7 +399,7 @@ class AbstractConfig(abc.ABC):
raise NotImplementedError
@abstractmethod
def set(self, key: str, val: Union[int, str, List[str], bool]) -> None:
def set(self, key: str, val: int | str | list[str] | bool) -> None:
"""
Set the given key's data to the given value.
+9 -10
View File
@@ -3,7 +3,6 @@ import os
import pathlib
import sys
from configparser import ConfigParser
from typing import List, Optional, Union
from config import AbstractConfig, appname, logger
@@ -18,7 +17,7 @@ class LinuxConfig(AbstractConfig):
__unescape_lut = {'\\': '\\', 'n': '\n', ';': ';', 'r': '\r', '#': '#'}
__escape_lut = {'\\': '\\', '\n': 'n', ';': ';', '\r': 'r'}
def __init__(self, filename: Optional[str] = None) -> None:
def __init__(self, filename: str | None = None) -> None:
super().__init__()
# http://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
xdg_data_home = pathlib.Path(os.getenv('XDG_DATA_HOME', default='~/.local/share')).expanduser()
@@ -42,7 +41,7 @@ class LinuxConfig(AbstractConfig):
self.filename.parent.mkdir(exist_ok=True, parents=True)
self.config: Optional[ConfigParser] = ConfigParser(comment_prefixes=('#',), interpolation=None)
self.config: ConfigParser | None = ConfigParser(comment_prefixes=('#',), interpolation=None)
self.config.read(self.filename) # read() ignores files that dont exist
# Ensure that our section exists. This is here because configparser will happily create files for us, but it
@@ -85,7 +84,7 @@ class LinuxConfig(AbstractConfig):
:param s: str - The string to unescape.
:return: str - The unescaped string.
"""
out: List[str] = []
out: list[str] = []
i = 0
while i < len(s):
c = s[i]
@@ -107,7 +106,7 @@ class LinuxConfig(AbstractConfig):
return "".join(out)
def __raw_get(self, key: str) -> Optional[str]:
def __raw_get(self, key: str) -> str | None:
"""
Get a raw data value from the config file.
@@ -119,7 +118,7 @@ class LinuxConfig(AbstractConfig):
return self.config[self.SECTION].get(key)
def get_str(self, key: str, *, default: str = None) -> str:
def get_str(self, key: str, *, default: str | None = None) -> str:
"""
Return the string referred to by the given key if it exists, or the default.
@@ -134,7 +133,7 @@ class LinuxConfig(AbstractConfig):
return self.__unescape(data)
def get_list(self, key: str, *, default: list = None) -> list:
def get_list(self, key: str, *, default: list | None = None) -> list:
"""
Return the list referred to by the given key if it exists, or the default.
@@ -168,7 +167,7 @@ class LinuxConfig(AbstractConfig):
except ValueError as e:
raise ValueError(f'requested {key=} as int cannot be converted to int') from e
def get_bool(self, key: str, *, default: bool = None) -> bool:
def get_bool(self, key: str, *, default: bool | None = None) -> bool:
"""
Return the bool referred to by the given key if it exists, or the default.
@@ -183,7 +182,7 @@ class LinuxConfig(AbstractConfig):
return bool(int(data))
def set(self, key: str, val: Union[int, str, List[str]]) -> None:
def set(self, key: str, val: int | str | list[str]) -> None:
"""
Set the given key's data to the given value.
@@ -192,7 +191,7 @@ class LinuxConfig(AbstractConfig):
if self.config is None:
raise ValueError('attempt to use a closed config')
to_set: Optional[str] = None
to_set: str | None = None
if isinstance(val, bool):
to_set = str(int(val))
+6 -5
View File
@@ -8,7 +8,7 @@ import sys
import uuid
import winreg
from ctypes.wintypes import DWORD, HANDLE
from typing import List, Optional, Union
from typing import List, Literal, Optional, Union
from config import AbstractConfig, applongname, appname, logger, update_interval
@@ -142,7 +142,7 @@ class WinConfig(AbstractConfig):
logger.warning(f'registry key {key=} returned unknown type {_type=} {value=}')
return None
def get_str(self, key: str, *, default: str = None) -> str:
def get_str(self, key: str, *, default: str | None = None) -> str:
"""
Return the string referred to by the given key if it exists, or the default.
@@ -157,7 +157,7 @@ class WinConfig(AbstractConfig):
return res
def get_list(self, key: str, *, default: list = None) -> list:
def get_list(self, key: str, *, default: list | None = None) -> list:
"""
Return the list referred to by the given key if it exists, or the default.
@@ -187,7 +187,7 @@ class WinConfig(AbstractConfig):
return res
def get_bool(self, key: str, *, default: bool = None) -> bool:
def get_bool(self, key: str, *, default: bool | None = None) -> bool:
"""
Return the bool referred to by the given key if it exists, or the default.
@@ -205,7 +205,8 @@ class WinConfig(AbstractConfig):
Implements :meth:`AbstractConfig.set`.
"""
reg_type = None
# These are the types that winreg.REG_* below resolve to.
reg_type: Literal[1] | Literal[4] | Literal[7]
if isinstance(val, str):
reg_type = winreg.REG_SZ
winreg.SetValueEx(self.__reg_handle, key, REG_RESERVED_ALWAYS_ZERO, winreg.REG_SZ, val)
File renamed without changes.
+1 -1
View File
@@ -167,7 +167,7 @@ class Dashboard(FileSystemEventHandler):
# Can get on_modified events when the file is emptied
self.process(event.src_path if not event.is_directory else None)
def process(self, logfile: str = None) -> None:
def process(self, logfile: str | None = None) -> None:
"""
Process the contents of current Status.json file.
+10 -3
View File
@@ -119,10 +119,17 @@ JSON primitives and their python equivalents
json compound types (`object -- {}` and `array -- []`) may be set.
### Testing
You can supply a custom killswitches file for testing against
EDMarketConnector:
```bash
python EDMarketConnector.py --killswitches-file <filename>
```
This will be relative to the CWD of the process.
Killswitch files can be tested using the script in `scripts/killswitch_test.py`.
Providing a file as an argument or `-` for stdin will output the behaviour of
the provided file, including indicating typos, if applicable.
Alternatively, killswitch files can be independently tested using the script in
`scripts/killswitch_test.py`. Providing a file as an argument or `-` for stdin
will output the behaviour of the provided file, including indicating typos, if
applicable.
### Versions
+11 -9
View File
@@ -191,15 +191,17 @@ that it's actually included in the installer.
Before you create a new install each time you should:
1. Ensure the data sourced from coriolis.io is up to date and works:
1. Update the `coriolis-data` repo. **NB: You will need 'npm' installed for
this.**
1. `cd coriolis-data`
1. `git pull`
1. `npm install` - to check it's worked.
1. Run `coriolis.py` to update `modules.p` and `ships.p`
1. XXX: Test ?
1. `git commit` the changes to the repo and the `.p` files.
1. Ensure translations are up to date, see [Translations.md](Translations.md).
2. Update the `coriolis-data` repo. **NB: You will need 'npm' installed for
this.**
1. `cd coriolis-data`
2. `git pull`
3. `npm install` - to check it's worked.
3. Run `coriolis-update-files.py` to update `modules.p` and `ships.p`. **NB:
The submodule might have been updated by a GitHub workflow/PR/merge, so
be sure to perform this step for every build.**
4. XXX: Test ?
5. `git commit` the changes to the repo and the `.p` files.
6. Ensure translations are up to date, see [Translations.md](Translations.md).
# Preparing to Package
+1 -1
View File
@@ -6,7 +6,7 @@ this is to include an XML-format .manifest file at build time.
## Build time
We specify .manifest files in setup.py.
We specify .manifest files in `Build-exe-and-msi.py`.
## Editing or changing a manifest
+3 -3
View File
@@ -27,7 +27,7 @@ class ClickCounter:
def __init__(self) -> None:
# Be sure to use names that wont collide in our config variables
self.click_count: Optional[tk.StringVar] = tk.StringVar(value=str(config.get_int('click_counter_count')))
self.click_count = tk.StringVar(value=str(config.get_int('click_counter_count')))
logger.info("ClickCounter instantiated")
def on_load(self) -> str:
@@ -99,8 +99,8 @@ class ClickCounter:
)
button.grid(row=current_row)
current_row += 1
nb.Label(frame, text="Count:").grid(row=current_row, sticky=tk.W)
nb.Label(frame, textvariable=self.click_count).grid(row=current_row, column=1)
tk.Label(frame, text="Count:").grid(row=current_row, sticky=tk.W)
tk.Label(frame, textvariable=self.click_count).grid(row=current_row, column=1)
return frame
-13
View File
@@ -13,19 +13,6 @@ from SubA import SubA
from config import appname, appversion, config
# For compatibility with pre-5.0.0
if not hasattr(config, 'get_int'):
config.get_int = config.getint
if not hasattr(config, 'get_str'):
config.get_str = config.get
if not hasattr(config, 'get_bool'):
config.get_bool = lambda key: bool(config.getint(key))
if not hasattr(config, 'get_list'):
config.get_list = config.get
# This could also be returned from plugin_start3()
plugin_name = os.path.basename(os.path.dirname(__file__))
+1 -1
View File
@@ -81,7 +81,7 @@ def export(data, filename=None) -> None: # noqa: C901, CCR001
if not v:
continue
module: __Module = outfitting.lookup(v['module'], ship_map)
module: __Module | None = outfitting.lookup(v['module'], ship_map)
if not module:
continue
-703
View File
@@ -1,703 +0,0 @@
"""Handle keyboard input for manual update triggering."""
# -*- coding: utf-8 -*-
import abc
import pathlib
import sys
import tkinter as tk
from abc import abstractmethod
from typing import Optional, Tuple, Union
from config import config
from EDMCLogging import get_main_logger
logger = get_main_logger()
class AbstractHotkeyMgr(abc.ABC):
"""Abstract root class of all platforms specific HotKeyMgr."""
@abstractmethod
def register(self, root, keycode, modifiers) -> None:
"""Register the hotkey handler."""
pass
@abstractmethod
def unregister(self) -> None:
"""Unregister the hotkey handling."""
pass
@abstractmethod
def play_good(self) -> None:
"""Play the 'good' sound."""
pass
@abstractmethod
def play_bad(self) -> None:
"""Play the 'bad' sound."""
pass
if sys.platform == 'darwin':
import objc
from AppKit import (
NSAlternateKeyMask, NSApplication, NSBeep, NSClearLineFunctionKey, NSCommandKeyMask, NSControlKeyMask,
NSDeleteFunctionKey, NSDeviceIndependentModifierFlagsMask, NSEvent, NSF1FunctionKey, NSF35FunctionKey,
NSFlagsChanged, NSKeyDown, NSKeyDownMask, NSKeyUp, NSNumericPadKeyMask, NSShiftKeyMask, NSSound, NSWorkspace
)
class MacHotkeyMgr(AbstractHotkeyMgr):
"""Hot key management."""
POLL = 250
# https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSEvent_Class/#//apple_ref/doc/constant_group/Function_Key_Unicodes
DISPLAY = {
0x03: u'⌅', 0x09: u'⇥', 0xd: u'↩', 0x19: u'⇤', 0x1b: u'esc', 0x20: u'⏘', 0x7f: u'⌫',
0xf700: u'↑', 0xf701: u'↓', 0xf702: u'←', 0xf703: u'→',
0xf727: u'Ins',
0xf728: u'⌦', 0xf729: u'↖', 0xf72a: u'Fn', 0xf72b: u'↘',
0xf72c: u'⇞', 0xf72d: u'⇟', 0xf72e: u'PrtScr', 0xf72f: u'ScrollLock',
0xf730: u'Pause', 0xf731: u'SysReq', 0xf732: u'Break', 0xf733: u'Reset',
0xf739: u'⌧',
}
(ACQUIRE_INACTIVE, ACQUIRE_ACTIVE, ACQUIRE_NEW) = range(3)
def __init__(self):
self.MODIFIERMASK = NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask \
| NSNumericPadKeyMask
self.root = None
self.keycode = 0
self.modifiers = 0
self.activated = False
self.observer = None
self.acquire_key = 0
self.acquire_state = MacHotkeyMgr.ACQUIRE_INACTIVE
self.tkProcessKeyEvent_old = None
self.snd_good = NSSound.alloc().initWithContentsOfFile_byReference_(
pathlib.Path(config.respath_path) / 'snd_good.wav', False
)
self.snd_bad = NSSound.alloc().initWithContentsOfFile_byReference_(
pathlib.Path(config.respath_path) / 'snd_bad.wav', False
)
def register(self, root: tk.Tk, keycode, modifiers) -> None:
"""
Register current hotkey for monitoring.
:param root: parent window.
:param keycode: Key to monitor.
:param modifiers: Any modifiers to take into account.
"""
self.root = root
self.keycode = keycode
self.modifiers = modifiers
self.activated = False
if keycode:
if not self.observer:
self.root.after_idle(self._observe)
self.root.after(MacHotkeyMgr.POLL, self._poll)
# Monkey-patch tk (tkMacOSXKeyEvent.c)
if not self.tkProcessKeyEvent_old:
sel = b'tkProcessKeyEvent:'
cls = NSApplication.sharedApplication().class__() # type: ignore
self.tkProcessKeyEvent_old = NSApplication.sharedApplication().methodForSelector_(sel) # type: ignore
newmethod = objc.selector( # type: ignore
self.tkProcessKeyEvent,
selector=self.tkProcessKeyEvent_old.selector,
signature=self.tkProcessKeyEvent_old.signature
)
objc.classAddMethod(cls, sel, newmethod) # type: ignore
def tkProcessKeyEvent(self, cls, the_event): # noqa: N802
"""
Monkey-patch tk (tkMacOSXKeyEvent.c).
- workaround crash on OSX 10.9 & 10.10 on seeing a composing character
- notice when modifier key state changes
- keep a copy of NSEvent.charactersIgnoringModifiers, which is what we need for the hotkey
(Would like to use a decorator but need to ensure the application is created before this is installed)
:param cls: ???
:param the_event: tk event
:return: ???
"""
if self.acquire_state:
if the_event.type() == NSFlagsChanged:
self.acquire_key = the_event.modifierFlags() & NSDeviceIndependentModifierFlagsMask
self.acquire_state = MacHotkeyMgr.ACQUIRE_NEW
# suppress the event by not chaining the old function
return the_event
elif the_event.type() in (NSKeyDown, NSKeyUp):
c = the_event.charactersIgnoringModifiers()
self.acquire_key = (c and ord(c[0]) or 0) | \
(the_event.modifierFlags() & NSDeviceIndependentModifierFlagsMask)
self.acquire_state = MacHotkeyMgr.ACQUIRE_NEW
# suppress the event by not chaining the old function
return the_event
# replace empty characters with charactersIgnoringModifiers to avoid crash
elif the_event.type() in (NSKeyDown, NSKeyUp) and not the_event.characters():
the_event = NSEvent.keyEventWithType_location_modifierFlags_timestamp_windowNumber_context_characters_charactersIgnoringModifiers_isARepeat_keyCode_( # noqa: E501
# noqa: E501
the_event.type(),
the_event.locationInWindow(),
the_event.modifierFlags(),
the_event.timestamp(),
the_event.windowNumber(),
the_event.context(),
the_event.charactersIgnoringModifiers(),
the_event.charactersIgnoringModifiers(),
the_event.isARepeat(),
the_event.keyCode()
)
return self.tkProcessKeyEvent_old(cls, the_event)
def _observe(self):
# Must be called after root.mainloop() so that the app's message loop has been created
self.observer = NSEvent.addGlobalMonitorForEventsMatchingMask_handler_(NSKeyDownMask, self._handler)
def _poll(self):
if config.shutting_down:
return
# No way of signalling to Tkinter from within the callback handler block that doesn't
# cause Python to crash, so poll.
if self.activated:
self.activated = False
self.root.event_generate('<<Invoke>>', when="tail")
if self.keycode or self.modifiers:
self.root.after(MacHotkeyMgr.POLL, self._poll)
def unregister(self) -> None:
"""Remove hotkey registration."""
self.keycode = None
self.modifiers = None
if sys.platform == 'darwin': # noqa: C901
@objc.callbackFor(NSEvent.addGlobalMonitorForEventsMatchingMask_handler_)
def _handler(self, event) -> None:
# use event.charactersIgnoringModifiers to handle composing characters like Alt-e
if ((event.modifierFlags() & self.MODIFIERMASK) == self.modifiers
and ord(event.charactersIgnoringModifiers()[0]) == self.keycode):
if config.get_int('hotkey_always'):
self.activated = True
else: # Only trigger if game client is front process
front = NSWorkspace.sharedWorkspace().frontmostApplication()
if front and front.bundleIdentifier() == 'uk.co.frontier.EliteDangerous':
self.activated = True
def acquire_start(self) -> None:
"""Start acquiring hotkey state via polling."""
self.acquire_state = MacHotkeyMgr.ACQUIRE_ACTIVE
self.root.after_idle(self._acquire_poll)
def acquire_stop(self) -> None:
"""Stop acquiring hotkey state."""
self.acquire_state = MacHotkeyMgr.ACQUIRE_INACTIVE
def _acquire_poll(self) -> None:
"""Perform a poll of current hotkey state."""
if config.shutting_down:
return
# No way of signalling to Tkinter from within the monkey-patched event handler that doesn't
# cause Python to crash, so poll.
if self.acquire_state:
if self.acquire_state == MacHotkeyMgr.ACQUIRE_NEW:
# Abuse tkEvent's keycode field to hold our acquired key & modifier
self.root.event_generate('<KeyPress>', keycode=self.acquire_key)
self.acquire_state = MacHotkeyMgr.ACQUIRE_ACTIVE
self.root.after(50, self._acquire_poll)
def fromevent(self, event) -> Optional[Union[bool, Tuple]]:
"""
Return configuration (keycode, modifiers) or None=clear or False=retain previous.
:param event: tk event ?
:return: False to retain previous, None to not use, else (keycode, modifiers)
"""
(keycode, modifiers) = (event.keycode & 0xffff, event.keycode & 0xffff0000) # Set by _acquire_poll()
if (keycode
and not (modifiers & (NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask))):
if keycode == 0x1b: # Esc = retain previous
self.acquire_state = MacHotkeyMgr.ACQUIRE_INACTIVE
return False
# BkSp, Del, Clear = clear hotkey
elif keycode in [0x7f, ord(NSDeleteFunctionKey), ord(NSClearLineFunctionKey)]:
self.acquire_state = MacHotkeyMgr.ACQUIRE_INACTIVE
return None
# don't allow keys needed for typing in System Map
elif keycode in [0x13, 0x20, 0x2d] or 0x61 <= keycode <= 0x7a:
NSBeep()
self.acquire_state = MacHotkeyMgr.ACQUIRE_INACTIVE
return None
return (keycode, modifiers)
def display(self, keycode, modifiers) -> str:
"""
Return displayable form of given hotkey + modifiers.
:param keycode:
:param modifiers:
:return: string form
"""
text = ''
if modifiers & NSControlKeyMask:
text += u'⌃'
if modifiers & NSAlternateKeyMask:
text += u'⌥'
if modifiers & NSShiftKeyMask:
text += u'⇧'
if modifiers & NSCommandKeyMask:
text += u'⌘'
if (modifiers & NSNumericPadKeyMask) and keycode <= 0x7f:
text += u'№'
if not keycode:
pass
elif ord(NSF1FunctionKey) <= keycode <= ord(NSF35FunctionKey):
text += f'F{keycode + 1 - ord(NSF1FunctionKey)}'
elif keycode in MacHotkeyMgr.DISPLAY: # specials
text += MacHotkeyMgr.DISPLAY[keycode]
elif keycode < 0x20: # control keys
text += chr(keycode + 0x40)
elif keycode < 0xf700: # key char
text += chr(keycode).upper()
else:
text += u'⁈'
return text
def play_good(self):
"""Play the 'good' sound."""
self.snd_good.play()
def play_bad(self):
"""Play the 'bad' sound."""
self.snd_bad.play()
if sys.platform == 'win32':
import atexit
import ctypes
import threading
import winsound
from ctypes.wintypes import DWORD, HWND, LONG, LPWSTR, MSG, ULONG, WORD
RegisterHotKey = ctypes.windll.user32.RegisterHotKey
UnregisterHotKey = ctypes.windll.user32.UnregisterHotKey
MOD_ALT = 0x0001
MOD_CONTROL = 0x0002
MOD_SHIFT = 0x0004
MOD_WIN = 0x0008
MOD_NOREPEAT = 0x4000
GetMessage = ctypes.windll.user32.GetMessageW
TranslateMessage = ctypes.windll.user32.TranslateMessage
DispatchMessage = ctypes.windll.user32.DispatchMessageW
PostThreadMessage = ctypes.windll.user32.PostThreadMessageW
WM_QUIT = 0x0012
WM_HOTKEY = 0x0312
WM_APP = 0x8000
WM_SND_GOOD = WM_APP + 1
WM_SND_BAD = WM_APP + 2
GetKeyState = ctypes.windll.user32.GetKeyState
MapVirtualKey = ctypes.windll.user32.MapVirtualKeyW
VK_BACK = 0x08
VK_CLEAR = 0x0c
VK_RETURN = 0x0d
VK_SHIFT = 0x10
VK_CONTROL = 0x11
VK_MENU = 0x12
VK_CAPITAL = 0x14
VK_MODECHANGE = 0x1f
VK_ESCAPE = 0x1b
VK_SPACE = 0x20
VK_DELETE = 0x2e
VK_LWIN = 0x5b
VK_RWIN = 0x5c
VK_NUMPAD0 = 0x60
VK_DIVIDE = 0x6f
VK_F1 = 0x70
VK_F24 = 0x87
VK_OEM_MINUS = 0xbd
VK_NUMLOCK = 0x90
VK_SCROLL = 0x91
VK_PROCESSKEY = 0xe5
VK_OEM_CLEAR = 0xfe
GetForegroundWindow = ctypes.windll.user32.GetForegroundWindow
GetWindowText = ctypes.windll.user32.GetWindowTextW
GetWindowText.argtypes = [HWND, LPWSTR, ctypes.c_int]
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
def window_title(h) -> str:
"""
Determine the title for a window.
:param h: Window handle.
:return: Window title.
"""
if h:
title_length = GetWindowTextLength(h) + 1
buf = ctypes.create_unicode_buffer(title_length)
if GetWindowText(h, buf, title_length):
return buf.value
return ''
class MOUSEINPUT(ctypes.Structure):
"""Mouse Input structure."""
_fields_ = [
('dx', LONG),
('dy', LONG),
('mouseData', DWORD),
('dwFlags', DWORD),
('time', DWORD),
('dwExtraInfo', ctypes.POINTER(ULONG))
]
class KEYBDINPUT(ctypes.Structure):
"""Keyboard Input structure."""
_fields_ = [
('wVk', WORD),
('wScan', WORD),
('dwFlags', DWORD),
('time', DWORD),
('dwExtraInfo', ctypes.POINTER(ULONG))
]
class HARDWAREINPUT(ctypes.Structure):
"""Hardware Input structure."""
_fields_ = [
('uMsg', DWORD),
('wParamL', WORD),
('wParamH', WORD)
]
class INPUTUNION(ctypes.Union):
"""Input union."""
_fields_ = [
('mi', MOUSEINPUT),
('ki', KEYBDINPUT),
('hi', HARDWAREINPUT)
]
class INPUT(ctypes.Structure):
"""Input structure."""
_fields_ = [
('type', DWORD),
('union', INPUTUNION)
]
SendInput = ctypes.windll.user32.SendInput
SendInput.argtypes = [ctypes.c_uint, ctypes.POINTER(INPUT), ctypes.c_int]
INPUT_MOUSE = 0
INPUT_KEYBOARD = 1
INPUT_HARDWARE = 2
class WindowsHotkeyMgr(AbstractHotkeyMgr):
"""Hot key management."""
# https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx
# Limit ourselves to symbols in Windows 7 Segoe UI
DISPLAY = {
0x03: 'Break', 0x08: 'Bksp', 0x09: u'↹', 0x0c: 'Clear', 0x0d: u'↵', 0x13: 'Pause',
0x14: u'Ⓐ', 0x1b: 'Esc',
0x20: u'⏘', 0x21: 'PgUp', 0x22: 'PgDn', 0x23: 'End', 0x24: 'Home',
0x25: u'←', 0x26: u'↑', 0x27: u'→', 0x28: u'↓',
0x2c: 'PrtScn', 0x2d: 'Ins', 0x2e: 'Del', 0x2f: 'Help',
0x5d: u'▤', 0x5f: u'☾',
0x90: u'➀', 0x91: 'ScrLk',
0xa6: u'⇦', 0xa7: u'⇨', 0xa9: u'⊗', 0xab: u'☆', 0xac: u'⌂', 0xb4: u'✉',
}
def __init__(self) -> None:
self.root: tk.Tk = None # type: ignore
self.thread: threading.Thread = None # type: ignore
with open(pathlib.Path(config.respath) / 'snd_good.wav', 'rb') as sg:
self.snd_good = sg.read()
with open(pathlib.Path(config.respath) / 'snd_bad.wav', 'rb') as sb:
self.snd_bad = sb.read()
atexit.register(self.unregister)
def register(self, root: tk.Tk, keycode, modifiers) -> None:
"""Register the hotkey handler."""
self.root = root
if self.thread:
logger.debug('Was already registered, unregistering...')
self.unregister()
if keycode or modifiers:
logger.debug('Creating thread worker...')
self.thread = threading.Thread(
target=self.worker,
name=f'Hotkey "{keycode}:{modifiers}"',
args=(keycode, modifiers)
)
self.thread.daemon = True
logger.debug('Starting thread worker...')
self.thread.start()
logger.debug('Done.')
def unregister(self) -> None:
"""Unregister the hotkey handling."""
thread = self.thread
if thread:
logger.debug('Thread is/was running')
self.thread = None # type: ignore
logger.debug('Telling thread WM_QUIT')
PostThreadMessage(thread.ident, WM_QUIT, 0, 0)
logger.debug('Joining thread')
thread.join() # Wait for it to unregister hotkey and quit
else:
logger.debug('No thread')
logger.debug('Done.')
def worker(self, keycode, modifiers) -> None: # noqa: CCR001
"""Handle hotkeys."""
logger.debug('Begin...')
# Hotkey must be registered by the thread that handles it
if not RegisterHotKey(None, 1, modifiers | MOD_NOREPEAT, keycode):
logger.debug("We're not the right thread?")
self.thread = None # type: ignore
return
fake = INPUT(INPUT_KEYBOARD, INPUTUNION(ki=KEYBDINPUT(keycode, keycode, 0, 0, None)))
msg = MSG()
logger.debug('Entering GetMessage() loop...')
while GetMessage(ctypes.byref(msg), None, 0, 0) != 0:
logger.debug('Got message')
if msg.message == WM_HOTKEY:
logger.debug('WM_HOTKEY')
if (
config.get_int('hotkey_always')
or window_title(GetForegroundWindow()).startswith('Elite - Dangerous')
):
if not config.shutting_down:
logger.debug('Sending event <<Invoke>>')
self.root.event_generate('<<Invoke>>', when="tail")
else:
logger.debug('Passing key on')
UnregisterHotKey(None, 1)
SendInput(1, fake, ctypes.sizeof(INPUT))
if not RegisterHotKey(None, 1, modifiers | MOD_NOREPEAT, keycode):
logger.debug("We aren't registered for this ?")
break
elif msg.message == WM_SND_GOOD:
logger.debug('WM_SND_GOOD')
winsound.PlaySound(self.snd_good, winsound.SND_MEMORY) # synchronous
elif msg.message == WM_SND_BAD:
logger.debug('WM_SND_BAD')
winsound.PlaySound(self.snd_bad, winsound.SND_MEMORY) # synchronous
else:
logger.debug('Something else')
TranslateMessage(ctypes.byref(msg))
DispatchMessage(ctypes.byref(msg))
logger.debug('Exited GetMessage() loop.')
UnregisterHotKey(None, 1)
self.thread = None # type: ignore
logger.debug('Done.')
def acquire_start(self) -> None:
"""Start acquiring hotkey state via polling."""
pass
def acquire_stop(self) -> None:
"""Stop acquiring hotkey state."""
pass
def fromevent(self, event) -> Optional[Union[bool, Tuple]]: # noqa: CCR001
"""
Return configuration (keycode, modifiers) or None=clear or False=retain previous.
event.state is a pain - it shows the state of the modifiers *before* a modifier key was pressed.
event.state *does* differentiate between left and right Ctrl and Alt and between Return and Enter
by putting KF_EXTENDED in bit 18, but RegisterHotKey doesn't differentiate.
:param event: tk event ?
:return: False to retain previous, None to not use, else (keycode, modifiers)
"""
modifiers = ((GetKeyState(VK_MENU) & 0x8000) and MOD_ALT) \
| ((GetKeyState(VK_CONTROL) & 0x8000) and MOD_CONTROL) \
| ((GetKeyState(VK_SHIFT) & 0x8000) and MOD_SHIFT) \
| ((GetKeyState(VK_LWIN) & 0x8000) and MOD_WIN) \
| ((GetKeyState(VK_RWIN) & 0x8000) and MOD_WIN)
keycode = event.keycode
if keycode in [VK_SHIFT, VK_CONTROL, VK_MENU, VK_LWIN, VK_RWIN]:
return (0, modifiers)
if not modifiers:
if keycode == VK_ESCAPE: # Esc = retain previous
return False
elif keycode in [VK_BACK, VK_DELETE, VK_CLEAR, VK_OEM_CLEAR]: # BkSp, Del, Clear = clear hotkey
return None
elif keycode in [VK_RETURN, VK_SPACE, VK_OEM_MINUS] or ord('A') <= keycode <= ord(
'Z'): # don't allow keys needed for typing in System Map
winsound.MessageBeep()
return None
elif (keycode in [VK_NUMLOCK, VK_SCROLL, VK_PROCESSKEY]
or VK_CAPITAL <= keycode <= VK_MODECHANGE): # ignore unmodified mode switch keys
return (0, modifiers)
# See if the keycode is usable and available
if RegisterHotKey(None, 2, modifiers | MOD_NOREPEAT, keycode):
UnregisterHotKey(None, 2)
return (keycode, modifiers)
else:
winsound.MessageBeep()
return None
def display(self, keycode, modifiers) -> str:
"""
Return displayable form of given hotkey + modifiers.
:param keycode:
:param modifiers:
:return: string form
"""
text = ''
if modifiers & MOD_WIN:
text += u'❖+'
if modifiers & MOD_CONTROL:
text += u'Ctrl+'
if modifiers & MOD_ALT:
text += u'Alt+'
if modifiers & MOD_SHIFT:
text += u'⇧+'
if VK_NUMPAD0 <= keycode <= VK_DIVIDE:
text += u'№'
if not keycode:
pass
elif VK_F1 <= keycode <= VK_F24:
text += f'F{keycode + 1 - VK_F1}'
elif keycode in WindowsHotkeyMgr.DISPLAY: # specials
text += WindowsHotkeyMgr.DISPLAY[keycode]
else:
c = MapVirtualKey(keycode, 2) # printable ?
if not c: # oops not printable
text += u'⁈'
elif c < 0x20: # control keys
text += chr(c + 0x40)
else:
text += chr(c).upper()
return text
def play_good(self) -> None:
"""Play the 'good' sound."""
if self.thread:
PostThreadMessage(self.thread.ident, WM_SND_GOOD, 0, 0)
def play_bad(self) -> None:
"""Play the 'bad' sound."""
if self.thread:
PostThreadMessage(self.thread.ident, WM_SND_BAD, 0, 0)
class LinuxHotKeyMgr(AbstractHotkeyMgr):
"""
Hot key management.
Not actually implemented on Linux. It's a no-op instead.
"""
def register(self, root, keycode, modifiers) -> None:
"""Register the hotkey handler."""
pass
def unregister(self) -> None:
"""Unregister the hotkey handling."""
pass
def play_good(self) -> None:
"""Play the 'good' sound."""
pass
def play_bad(self) -> None:
"""Play the 'bad' sound."""
pass
def get_hotkeymgr() -> AbstractHotkeyMgr:
"""
Determine platform-specific HotkeyMgr.
:param args:
:param kwargs:
:return: Appropriate class instance.
:raises ValueError: If unsupported platform.
"""
if sys.platform == 'darwin':
return MacHotkeyMgr()
elif sys.platform == 'win32':
return WindowsHotkeyMgr()
elif sys.platform == 'linux':
return LinuxHotKeyMgr()
else:
raise ValueError(f'Unknown platform: {sys.platform}')
# singleton
hotkeymgr = get_hotkeymgr()
+95
View File
@@ -0,0 +1,95 @@
"""Handle keyboard input for manual update triggering."""
# -*- coding: utf-8 -*-
import abc
import sys
from abc import abstractmethod
from typing import Optional, Tuple, Union
class AbstractHotkeyMgr(abc.ABC):
"""Abstract root class of all platforms specific HotKeyMgr."""
@abstractmethod
def register(self, root, keycode, modifiers) -> None:
"""Register the hotkey handler."""
pass
@abstractmethod
def unregister(self) -> None:
"""Unregister the hotkey handling."""
pass
@abstractmethod
def acquire_start(self) -> None:
"""Start acquiring hotkey state via polling."""
pass
@abstractmethod
def acquire_stop(self) -> None:
"""Stop acquiring hotkey state."""
pass
@abstractmethod
def fromevent(self, event) -> Optional[Union[bool, Tuple]]:
"""
Return configuration (keycode, modifiers) or None=clear or False=retain previous.
event.state is a pain - it shows the state of the modifiers *before* a modifier key was pressed.
event.state *does* differentiate between left and right Ctrl and Alt and between Return and Enter
by putting KF_EXTENDED in bit 18, but RegisterHotKey doesn't differentiate.
:param event: tk event ?
:return: False to retain previous, None to not use, else (keycode, modifiers)
"""
pass
@abstractmethod
def display(self, keycode: int, modifiers: int) -> str:
"""
Return displayable form of given hotkey + modifiers.
:param keycode:
:param modifiers:
:return: string form
"""
pass
@abstractmethod
def play_good(self) -> None:
"""Play the 'good' sound."""
pass
@abstractmethod
def play_bad(self) -> None:
"""Play the 'bad' sound."""
pass
def get_hotkeymgr() -> AbstractHotkeyMgr:
"""
Determine platform-specific HotkeyMgr.
:param args:
:param kwargs:
:return: Appropriate class instance.
:raises ValueError: If unsupported platform.
"""
if sys.platform == 'darwin':
from hotkey.darwin import MacHotkeyMgr
return MacHotkeyMgr()
elif sys.platform == 'win32':
from hotkey.windows import WindowsHotkeyMgr
return WindowsHotkeyMgr()
elif sys.platform == 'linux':
from hotkey.linux import LinuxHotKeyMgr
return LinuxHotKeyMgr()
else:
raise ValueError(f'Unknown platform: {sys.platform}')
# singleton
hotkeymgr = get_hotkeymgr()
+274
View File
@@ -0,0 +1,274 @@
"""darwin/macOS implementation of hotkey.AbstractHotkeyMgr."""
import pathlib
import sys
import tkinter as tk
from typing import Callable, Optional, Tuple, Union
assert sys.platform == 'darwin'
import objc
from AppKit import (
NSAlternateKeyMask, NSApplication, NSBeep, NSClearLineFunctionKey, NSCommandKeyMask, NSControlKeyMask,
NSDeleteFunctionKey, NSDeviceIndependentModifierFlagsMask, NSEvent, NSF1FunctionKey, NSF35FunctionKey,
NSFlagsChanged, NSKeyDown, NSKeyDownMask, NSKeyUp, NSNumericPadKeyMask, NSShiftKeyMask, NSSound, NSWorkspace
)
from config import config
from EDMCLogging import get_main_logger
from hotkey import AbstractHotkeyMgr
logger = get_main_logger()
class MacHotkeyMgr(AbstractHotkeyMgr):
"""Hot key management."""
POLL = 250
# https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSEvent_Class/#//apple_ref/doc/constant_group/Function_Key_Unicodes
DISPLAY = {
0x03: u'⌅', 0x09: u'⇥', 0xd: u'↩', 0x19: u'⇤', 0x1b: u'esc', 0x20: u'⏘', 0x7f: u'⌫',
0xf700: u'↑', 0xf701: u'↓', 0xf702: u'←', 0xf703: u'→',
0xf727: u'Ins',
0xf728: u'⌦', 0xf729: u'↖', 0xf72a: u'Fn', 0xf72b: u'↘',
0xf72c: u'⇞', 0xf72d: u'⇟', 0xf72e: u'PrtScr', 0xf72f: u'ScrollLock',
0xf730: u'Pause', 0xf731: u'SysReq', 0xf732: u'Break', 0xf733: u'Reset',
0xf739: u'⌧',
}
(ACQUIRE_INACTIVE, ACQUIRE_ACTIVE, ACQUIRE_NEW) = range(3)
def __init__(self):
self.MODIFIERMASK = NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask \
| NSNumericPadKeyMask
self.root: tk.Tk
self.keycode = 0
self.modifiers = 0
self.activated = False
self.observer = None
self.acquire_key = 0
self.acquire_state = MacHotkeyMgr.ACQUIRE_INACTIVE
self.tkProcessKeyEvent_old: Callable
self.snd_good = NSSound.alloc().initWithContentsOfFile_byReference_(
pathlib.Path(config.respath_path) / 'snd_good.wav', False
)
self.snd_bad = NSSound.alloc().initWithContentsOfFile_byReference_(
pathlib.Path(config.respath_path) / 'snd_bad.wav', False
)
def register(self, root: tk.Tk, keycode: int, modifiers: int) -> None:
"""
Register current hotkey for monitoring.
:param root: parent window.
:param keycode: Key to monitor.
:param modifiers: Any modifiers to take into account.
"""
self.root = root
self.keycode = keycode
self.modifiers = modifiers
self.activated = False
if keycode:
if not self.observer:
self.root.after_idle(self._observe)
self.root.after(MacHotkeyMgr.POLL, self._poll)
# Monkey-patch tk (tkMacOSXKeyEvent.c)
if not callable(self.tkProcessKeyEvent_old):
sel = b'tkProcessKeyEvent:'
cls = NSApplication.sharedApplication().class__() # type: ignore
self.tkProcessKeyEvent_old = NSApplication.sharedApplication().methodForSelector_(sel) # type: ignore
newmethod = objc.selector( # type: ignore
self.tkProcessKeyEvent,
selector=self.tkProcessKeyEvent_old.selector,
signature=self.tkProcessKeyEvent_old.signature
)
objc.classAddMethod(cls, sel, newmethod) # type: ignore
def tkProcessKeyEvent(self, cls, the_event): # noqa: N802
"""
Monkey-patch tk (tkMacOSXKeyEvent.c).
- workaround crash on OSX 10.9 & 10.10 on seeing a composing character
- notice when modifier key state changes
- keep a copy of NSEvent.charactersIgnoringModifiers, which is what we need for the hotkey
(Would like to use a decorator but need to ensure the application is created before this is installed)
:param cls: ???
:param the_event: tk event
:return: ???
"""
if self.acquire_state:
if the_event.type() == NSFlagsChanged:
self.acquire_key = the_event.modifierFlags() & NSDeviceIndependentModifierFlagsMask
self.acquire_state = MacHotkeyMgr.ACQUIRE_NEW
# suppress the event by not chaining the old function
return the_event
elif the_event.type() in (NSKeyDown, NSKeyUp):
c = the_event.charactersIgnoringModifiers()
self.acquire_key = (c and ord(c[0]) or 0) | \
(the_event.modifierFlags() & NSDeviceIndependentModifierFlagsMask)
self.acquire_state = MacHotkeyMgr.ACQUIRE_NEW
# suppress the event by not chaining the old function
return the_event
# replace empty characters with charactersIgnoringModifiers to avoid crash
elif the_event.type() in (NSKeyDown, NSKeyUp) and not the_event.characters():
the_event = NSEvent.keyEventWithType_location_modifierFlags_timestamp_windowNumber_context_characters_charactersIgnoringModifiers_isARepeat_keyCode_( # noqa: E501
# noqa: E501
the_event.type(),
the_event.locationInWindow(),
the_event.modifierFlags(),
the_event.timestamp(),
the_event.windowNumber(),
the_event.context(),
the_event.charactersIgnoringModifiers(),
the_event.charactersIgnoringModifiers(),
the_event.isARepeat(),
the_event.keyCode()
)
return self.tkProcessKeyEvent_old(cls, the_event)
def _observe(self):
# Must be called after root.mainloop() so that the app's message loop has been created
self.observer = NSEvent.addGlobalMonitorForEventsMatchingMask_handler_(NSKeyDownMask, self._handler)
def _poll(self):
if config.shutting_down:
return
# No way of signalling to Tkinter from within the callback handler block that doesn't
# cause Python to crash, so poll.
if self.activated:
self.activated = False
self.root.event_generate('<<Invoke>>', when="tail")
if self.keycode or self.modifiers:
self.root.after(MacHotkeyMgr.POLL, self._poll)
def unregister(self) -> None:
"""Remove hotkey registration."""
self.keycode = 0
self.modifiers = 0
@objc.callbackFor(NSEvent.addGlobalMonitorForEventsMatchingMask_handler_)
def _handler(self, event) -> None:
# use event.charactersIgnoringModifiers to handle composing characters like Alt-e
if (
(event.modifierFlags() & self.MODIFIERMASK) == self.modifiers
and ord(event.charactersIgnoringModifiers()[0]) == self.keycode
):
if config.get_int('hotkey_always'):
self.activated = True
else: # Only trigger if game client is front process
front = NSWorkspace.sharedWorkspace().frontmostApplication()
if front and front.bundleIdentifier() == 'uk.co.frontier.EliteDangerous':
self.activated = True
def acquire_start(self) -> None:
"""Start acquiring hotkey state via polling."""
self.acquire_state = MacHotkeyMgr.ACQUIRE_ACTIVE
self.root.after_idle(self._acquire_poll)
def acquire_stop(self) -> None:
"""Stop acquiring hotkey state."""
self.acquire_state = MacHotkeyMgr.ACQUIRE_INACTIVE
def _acquire_poll(self) -> None:
"""Perform a poll of current hotkey state."""
if config.shutting_down:
return
# No way of signalling to Tkinter from within the monkey-patched event handler that doesn't
# cause Python to crash, so poll.
if self.acquire_state:
if self.acquire_state == MacHotkeyMgr.ACQUIRE_NEW:
# Abuse tkEvent's keycode field to hold our acquired key & modifier
self.root.event_generate('<KeyPress>', keycode=self.acquire_key)
self.acquire_state = MacHotkeyMgr.ACQUIRE_ACTIVE
self.root.after(50, self._acquire_poll)
def fromevent(self, event) -> Optional[Union[bool, Tuple]]:
"""
Return configuration (keycode, modifiers) or None=clear or False=retain previous.
:param event: tk event ?
:return: False to retain previous, None to not use, else (keycode, modifiers)
"""
(keycode, modifiers) = (event.keycode & 0xffff, event.keycode & 0xffff0000) # Set by _acquire_poll()
if (
keycode
and not (modifiers & (NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask))
):
if keycode == 0x1b: # Esc = retain previous
self.acquire_state = MacHotkeyMgr.ACQUIRE_INACTIVE
return False
# BkSp, Del, Clear = clear hotkey
elif keycode in [0x7f, ord(NSDeleteFunctionKey), ord(NSClearLineFunctionKey)]:
self.acquire_state = MacHotkeyMgr.ACQUIRE_INACTIVE
return None
# don't allow keys needed for typing in System Map
elif keycode in [0x13, 0x20, 0x2d] or 0x61 <= keycode <= 0x7a:
NSBeep()
self.acquire_state = MacHotkeyMgr.ACQUIRE_INACTIVE
return None
return (keycode, modifiers)
def display(self, keycode, modifiers) -> str:
"""
Return displayable form of given hotkey + modifiers.
:param keycode:
:param modifiers:
:return: string form
"""
text = ''
if modifiers & NSControlKeyMask:
text += u'⌃'
if modifiers & NSAlternateKeyMask:
text += u'⌥'
if modifiers & NSShiftKeyMask:
text += u'⇧'
if modifiers & NSCommandKeyMask:
text += u'⌘'
if (modifiers & NSNumericPadKeyMask) and keycode <= 0x7f:
text += u'№'
if not keycode:
pass
elif ord(NSF1FunctionKey) <= keycode <= ord(NSF35FunctionKey):
text += f'F{keycode + 1 - ord(NSF1FunctionKey)}'
elif keycode in MacHotkeyMgr.DISPLAY: # specials
text += MacHotkeyMgr.DISPLAY[keycode]
elif keycode < 0x20: # control keys
text += chr(keycode + 0x40)
elif keycode < 0xf700: # key char
text += chr(keycode).upper()
else:
text += u'⁈'
return text
def play_good(self):
"""Play the 'good' sound."""
self.snd_good.play()
def play_bad(self):
"""Play the 'bad' sound."""
self.snd_bad.play()
+64
View File
@@ -0,0 +1,64 @@
"""Linux implementation of hotkey.AbstractHotkeyMgr."""
import sys
from EDMCLogging import get_main_logger
from hotkey import AbstractHotkeyMgr
assert sys.platform == 'linux'
logger = get_main_logger()
class LinuxHotKeyMgr(AbstractHotkeyMgr):
"""
Hot key management.
Not actually implemented on Linux. It's a no-op instead.
"""
def register(self, root, keycode, modifiers) -> None:
"""Register the hotkey handler."""
pass
def unregister(self) -> None:
"""Unregister the hotkey handling."""
pass
def acquire_start(self) -> None:
"""Start acquiring hotkey state via polling."""
pass
def acquire_stop(self) -> None:
"""Stop acquiring hotkey state."""
pass
def fromevent(self, event) -> bool | tuple | None:
"""
Return configuration (keycode, modifiers) or None=clear or False=retain previous.
event.state is a pain - it shows the state of the modifiers *before* a modifier key was pressed.
event.state *does* differentiate between left and right Ctrl and Alt and between Return and Enter
by putting KF_EXTENDED in bit 18, but RegisterHotKey doesn't differentiate.
:param event: tk event ?
:return: False to retain previous, None to not use, else (keycode, modifiers)
"""
pass
def display(self, keycode: int, modifiers: int) -> str:
"""
Return displayable form of given hotkey + modifiers.
:param keycode:
:param modifiers:
:return: string form
"""
return "Unsupported on linux"
def play_good(self) -> None:
"""Play the 'good' sound."""
pass
def play_bad(self) -> None:
"""Play the 'bad' sound."""
pass
+370
View File
@@ -0,0 +1,370 @@
"""Windows implementation of hotkey.AbstractHotkeyMgr."""
import atexit
import ctypes
import pathlib
import sys
import threading
import tkinter as tk
import winsound
from ctypes.wintypes import DWORD, HWND, LONG, LPWSTR, MSG, ULONG, WORD
from typing import Optional, Tuple, Union
from config import config
from EDMCLogging import get_main_logger
from hotkey import AbstractHotkeyMgr
assert sys.platform == 'win32'
logger = get_main_logger()
RegisterHotKey = ctypes.windll.user32.RegisterHotKey
UnregisterHotKey = ctypes.windll.user32.UnregisterHotKey
MOD_ALT = 0x0001
MOD_CONTROL = 0x0002
MOD_SHIFT = 0x0004
MOD_WIN = 0x0008
MOD_NOREPEAT = 0x4000
GetMessage = ctypes.windll.user32.GetMessageW
TranslateMessage = ctypes.windll.user32.TranslateMessage
DispatchMessage = ctypes.windll.user32.DispatchMessageW
PostThreadMessage = ctypes.windll.user32.PostThreadMessageW
WM_QUIT = 0x0012
WM_HOTKEY = 0x0312
WM_APP = 0x8000
WM_SND_GOOD = WM_APP + 1
WM_SND_BAD = WM_APP + 2
GetKeyState = ctypes.windll.user32.GetKeyState
MapVirtualKey = ctypes.windll.user32.MapVirtualKeyW
VK_BACK = 0x08
VK_CLEAR = 0x0c
VK_RETURN = 0x0d
VK_SHIFT = 0x10
VK_CONTROL = 0x11
VK_MENU = 0x12
VK_CAPITAL = 0x14
VK_MODECHANGE = 0x1f
VK_ESCAPE = 0x1b
VK_SPACE = 0x20
VK_DELETE = 0x2e
VK_LWIN = 0x5b
VK_RWIN = 0x5c
VK_NUMPAD0 = 0x60
VK_DIVIDE = 0x6f
VK_F1 = 0x70
VK_F24 = 0x87
VK_OEM_MINUS = 0xbd
VK_NUMLOCK = 0x90
VK_SCROLL = 0x91
VK_PROCESSKEY = 0xe5
VK_OEM_CLEAR = 0xfe
GetForegroundWindow = ctypes.windll.user32.GetForegroundWindow
GetWindowText = ctypes.windll.user32.GetWindowTextW
GetWindowText.argtypes = [HWND, LPWSTR, ctypes.c_int]
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
def window_title(h) -> str:
"""
Determine the title for a window.
:param h: Window handle.
:return: Window title.
"""
if h:
title_length = GetWindowTextLength(h) + 1
buf = ctypes.create_unicode_buffer(title_length)
if GetWindowText(h, buf, title_length):
return buf.value
return ''
class MOUSEINPUT(ctypes.Structure):
"""Mouse Input structure."""
_fields_ = [
('dx', LONG),
('dy', LONG),
('mouseData', DWORD),
('dwFlags', DWORD),
('time', DWORD),
('dwExtraInfo', ctypes.POINTER(ULONG))
]
class KEYBDINPUT(ctypes.Structure):
"""Keyboard Input structure."""
_fields_ = [
('wVk', WORD),
('wScan', WORD),
('dwFlags', DWORD),
('time', DWORD),
('dwExtraInfo', ctypes.POINTER(ULONG))
]
class HARDWAREINPUT(ctypes.Structure):
"""Hardware Input structure."""
_fields_ = [
('uMsg', DWORD),
('wParamL', WORD),
('wParamH', WORD)
]
class INPUTUNION(ctypes.Union):
"""Input union."""
_fields_ = [
('mi', MOUSEINPUT),
('ki', KEYBDINPUT),
('hi', HARDWAREINPUT)
]
class INPUT(ctypes.Structure):
"""Input structure."""
_fields_ = [
('type', DWORD),
('union', INPUTUNION)
]
SendInput = ctypes.windll.user32.SendInput
SendInput.argtypes = [ctypes.c_uint, ctypes.POINTER(INPUT), ctypes.c_int]
INPUT_MOUSE = 0
INPUT_KEYBOARD = 1
INPUT_HARDWARE = 2
class WindowsHotkeyMgr(AbstractHotkeyMgr):
"""Hot key management."""
# https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx
# Limit ourselves to symbols in Windows 7 Segoe UI
DISPLAY = {
0x03: 'Break', 0x08: 'Bksp', 0x09: '↹', 0x0c: 'Clear', 0x0d: '↵', 0x13: 'Pause',
0x14: 'Ⓐ', 0x1b: 'Esc',
0x20: '⏘', 0x21: 'PgUp', 0x22: 'PgDn', 0x23: 'End', 0x24: 'Home',
0x25: '←', 0x26: '↑', 0x27: '→', 0x28: '↓',
0x2c: 'PrtScn', 0x2d: 'Ins', 0x2e: 'Del', 0x2f: 'Help',
0x5d: '▤', 0x5f: '☾',
0x90: '➀', 0x91: 'ScrLk',
0xa6: '⇦', 0xa7: '⇨', 0xa9: '⊗', 0xab: '☆', 0xac: '⌂', 0xb4: '✉',
}
def __init__(self) -> None:
self.root: tk.Tk = None # type: ignore
self.thread: threading.Thread = None # type: ignore
with open(pathlib.Path(config.respath) / 'snd_good.wav', 'rb') as sg:
self.snd_good = sg.read()
with open(pathlib.Path(config.respath) / 'snd_bad.wav', 'rb') as sb:
self.snd_bad = sb.read()
atexit.register(self.unregister)
def register(self, root: tk.Tk, keycode, modifiers) -> None:
"""Register the hotkey handler."""
self.root = root
if self.thread:
logger.debug('Was already registered, unregistering...')
self.unregister()
if keycode or modifiers:
logger.debug('Creating thread worker...')
self.thread = threading.Thread(
target=self.worker,
name=f'Hotkey "{keycode}:{modifiers}"',
args=(keycode, modifiers)
)
self.thread.daemon = True
logger.debug('Starting thread worker...')
self.thread.start()
logger.debug('Done.')
def unregister(self) -> None:
"""Unregister the hotkey handling."""
thread = self.thread
if thread:
logger.debug('Thread is/was running')
self.thread = None # type: ignore
logger.debug('Telling thread WM_QUIT')
PostThreadMessage(thread.ident, WM_QUIT, 0, 0)
logger.debug('Joining thread')
thread.join() # Wait for it to unregister hotkey and quit
else:
logger.debug('No thread')
logger.debug('Done.')
def worker(self, keycode, modifiers) -> None: # noqa: CCR001
"""Handle hotkeys."""
logger.debug('Begin...')
# Hotkey must be registered by the thread that handles it
if not RegisterHotKey(None, 1, modifiers | MOD_NOREPEAT, keycode):
logger.debug("We're not the right thread?")
self.thread = None # type: ignore
return
fake = INPUT(INPUT_KEYBOARD, INPUTUNION(ki=KEYBDINPUT(keycode, keycode, 0, 0, None)))
msg = MSG()
logger.debug('Entering GetMessage() loop...')
while GetMessage(ctypes.byref(msg), None, 0, 0) != 0:
logger.debug('Got message')
if msg.message == WM_HOTKEY:
logger.debug('WM_HOTKEY')
if (
config.get_int('hotkey_always')
or window_title(GetForegroundWindow()).startswith('Elite - Dangerous')
):
if not config.shutting_down:
logger.debug('Sending event <<Invoke>>')
self.root.event_generate('<<Invoke>>', when="tail")
else:
logger.debug('Passing key on')
UnregisterHotKey(None, 1)
SendInput(1, fake, ctypes.sizeof(INPUT))
if not RegisterHotKey(None, 1, modifiers | MOD_NOREPEAT, keycode):
logger.debug("We aren't registered for this ?")
break
elif msg.message == WM_SND_GOOD:
logger.debug('WM_SND_GOOD')
winsound.PlaySound(self.snd_good, winsound.SND_MEMORY) # synchronous
elif msg.message == WM_SND_BAD:
logger.debug('WM_SND_BAD')
winsound.PlaySound(self.snd_bad, winsound.SND_MEMORY) # synchronous
else:
logger.debug('Something else')
TranslateMessage(ctypes.byref(msg))
DispatchMessage(ctypes.byref(msg))
logger.debug('Exited GetMessage() loop.')
UnregisterHotKey(None, 1)
self.thread = None # type: ignore
logger.debug('Done.')
def acquire_start(self) -> None:
"""Start acquiring hotkey state via polling."""
pass
def acquire_stop(self) -> None:
"""Stop acquiring hotkey state."""
pass
def fromevent(self, event) -> Optional[Union[bool, Tuple]]: # noqa: CCR001
"""
Return configuration (keycode, modifiers) or None=clear or False=retain previous.
event.state is a pain - it shows the state of the modifiers *before* a modifier key was pressed.
event.state *does* differentiate between left and right Ctrl and Alt and between Return and Enter
by putting KF_EXTENDED in bit 18, but RegisterHotKey doesn't differentiate.
:param event: tk event ?
:return: False to retain previous, None to not use, else (keycode, modifiers)
"""
modifiers = ((GetKeyState(VK_MENU) & 0x8000) and MOD_ALT) \
| ((GetKeyState(VK_CONTROL) & 0x8000) and MOD_CONTROL) \
| ((GetKeyState(VK_SHIFT) & 0x8000) and MOD_SHIFT) \
| ((GetKeyState(VK_LWIN) & 0x8000) and MOD_WIN) \
| ((GetKeyState(VK_RWIN) & 0x8000) and MOD_WIN)
keycode = event.keycode
if keycode in [VK_SHIFT, VK_CONTROL, VK_MENU, VK_LWIN, VK_RWIN]:
return (0, modifiers)
if not modifiers:
if keycode == VK_ESCAPE: # Esc = retain previous
return False
elif keycode in [VK_BACK, VK_DELETE, VK_CLEAR, VK_OEM_CLEAR]: # BkSp, Del, Clear = clear hotkey
return None
elif (
keycode in [VK_RETURN, VK_SPACE, VK_OEM_MINUS] or ord('A') <= keycode <= ord('Z')
): # don't allow keys needed for typing in System Map
winsound.MessageBeep()
return None
elif (keycode in [VK_NUMLOCK, VK_SCROLL, VK_PROCESSKEY]
or VK_CAPITAL <= keycode <= VK_MODECHANGE): # ignore unmodified mode switch keys
return (0, modifiers)
# See if the keycode is usable and available
if RegisterHotKey(None, 2, modifiers | MOD_NOREPEAT, keycode):
UnregisterHotKey(None, 2)
return (keycode, modifiers)
else:
winsound.MessageBeep()
return None
def display(self, keycode, modifiers) -> str:
"""
Return displayable form of given hotkey + modifiers.
:param keycode:
:param modifiers:
:return: string form
"""
text = ''
if modifiers & MOD_WIN:
text += '❖+'
if modifiers & MOD_CONTROL:
text += 'Ctrl+'
if modifiers & MOD_ALT:
text += 'Alt+'
if modifiers & MOD_SHIFT:
text += '⇧+'
if VK_NUMPAD0 <= keycode <= VK_DIVIDE:
text += '№'
if not keycode:
pass
elif VK_F1 <= keycode <= VK_F24:
text += f'F{keycode + 1 - VK_F1}'
elif keycode in WindowsHotkeyMgr.DISPLAY: # specials
text += WindowsHotkeyMgr.DISPLAY[keycode]
else:
c = MapVirtualKey(keycode, 2) # printable ?
if not c: # oops not printable
text += '⁈'
elif c < 0x20: # control keys
text += chr(c + 0x40)
else:
text += chr(c).upper()
return text
def play_good(self) -> None:
"""Play the 'good' sound."""
if self.thread:
PostThreadMessage(self.thread.ident, WM_SND_GOOD, 0, 0)
def play_bad(self) -> None:
"""Play the 'bad' sound."""
if self.thread:
PostThreadMessage(self.thread.ident, WM_SND_BAD, 0, 0)
+13
View File
@@ -0,0 +1,13 @@
#!/usr/bin/env xdg-open
[Desktop Entry]
Type=Application
# Ref: <https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.5.html>
Version=1.5
Name=E:D Market Connector
Comment=Utilise Elite Dangerous Journal and CAPI data
Icon=io.edcd.EDMarketConnector
Categories=Game;
Keywords=Elite;Dangerous;EDDN;EDSM;EDDB;Inara;Spansh;
Exec=edmarketconnector
Terminal=false
SingleMainWindow=true
File renamed without changes.
+1 -1
View File
@@ -33,7 +33,7 @@ class JournalLock:
def __init__(self) -> None:
"""Initialise where the journal directory and lock file are."""
self.journal_dir: str = config.get_str('journaldir') or config.default_journal_dir
self.journal_dir: str | None = config.get_str('journaldir') or config.default_journal_dir
self.journal_dir_path: Optional[pathlib.Path] = None
self.set_path_from_journaldir()
self.journal_dir_lockfile_name: Optional[pathlib.Path] = None
+16 -2
View File
@@ -1,6 +1,7 @@
"""Fetch kill switches from EDMC Repo."""
from __future__ import annotations
import json
import threading
from copy import deepcopy
from typing import (
@@ -339,6 +340,16 @@ def fetch_kill_switches(target=DEFAULT_KILLSWITCH_URL) -> Optional[KillSwitchJSO
:return: a list of dicts containing kill switch data, or None
"""
logger.info("Attempting to fetch kill switches")
if target.startswith('file:'):
target = target.replace('file:', '')
try:
with open(target, 'r') as t:
return json.load(t)
except FileNotFoundError:
logger.warning(f"No such file '{target}'")
return None
try:
data = requests.get(target, timeout=10).json()
@@ -458,13 +469,16 @@ def get_kill_switches_thread(
active: KillSwitchSet = KillSwitchSet([])
def setup_main_list():
def setup_main_list(filename: Optional[str]):
"""
Set up the global set of kill switches for querying.
Plugins should NOT call this EVER.
"""
if (data := get_kill_switches(DEFAULT_KILLSWITCH_URL, OLD_KILLSWITCH_URL)) is None:
if filename is None:
filename = DEFAULT_KILLSWITCH_URL
if (data := get_kill_switches(filename, OLD_KILLSWITCH_URL)) is None:
logger.warning("Unable to fetch kill switches. Setting global set to an empty set")
return
+2 -2
View File
@@ -83,7 +83,7 @@ class _Translations:
self.translations = {None: {}}
builtins.__dict__['_'] = lambda x: str(x).replace(r'\"', '"').replace('{CR}', '\n')
def install(self, lang: str = None) -> None: # noqa: CCR001
def install(self, lang: str | None = None) -> None: # noqa: CCR001
"""
Install the translation function to the _ builtin.
@@ -250,7 +250,7 @@ class _Locale:
self.float_formatter.setMinimumFractionDigits_(5)
self.float_formatter.setMaximumFractionDigits_(5)
def stringFromNumber(self, number: Union[float, int], decimals: int = None) -> str: # noqa: N802
def stringFromNumber(self, number: Union[float, int], decimals: int | None = None) -> str: # noqa: N802
warnings.warn(DeprecationWarning('use _Locale.string_from_number instead.'))
return self.string_from_number(number, decimals) # type: ignore
+307 -187
View File
@@ -14,9 +14,7 @@ from collections import OrderedDict, defaultdict
from os import SEEK_END, SEEK_SET, listdir
from os.path import basename, expanduser, getctime, isdir, join
from time import gmtime, localtime, mktime, sleep, strftime, strptime, time
from typing import TYPE_CHECKING, Any, BinaryIO, Dict, List, MutableMapping, Optional
from typing import OrderedDict as OrderedDictT
from typing import Tuple
from typing import TYPE_CHECKING, Any, BinaryIO, MutableMapping, Tuple
if TYPE_CHECKING:
import tkinter
@@ -91,11 +89,11 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
# TODO(A_D): A bunch of these should be switched to default values (eg '' for strings) and no longer be Optional
FileSystemEventHandler.__init__(self) # futureproofing - not need for current version of watchdog
self.root: 'tkinter.Tk' = None # type: ignore # Don't use Optional[] - mypy thinks no methods
self.currentdir: Optional[str] = None # The actual logdir that we're monitoring
self.logfile: Optional[str] = None
self.observer: Optional['Observer'] = None
self.currentdir: str | None = None # The actual logdir that we're monitoring
self.logfile: str | None = None
self.observer: 'Observer' | None = None
self.observed = None # a watchdog ObservedWatch, or None if polling
self.thread: Optional[threading.Thread] = None
self.thread: threading.Thread | None = None
# For communicating journal entries back to main thread
self.event_queue: queue.Queue = queue.Queue(maxsize=0)
@@ -112,27 +110,19 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
self.game_was_running = False # For generation of the "ShutDown" event
# Context for journal handling
self.version: Optional[str] = None
self.version_semantic: Optional[semantic_version.Version] = None
self.version: str | None = None
self.version_semantic: semantic_version.Version | None = None
self.is_beta = False
self.mode: Optional[str] = None
self.group: Optional[str] = None
self.cmdr: Optional[str] = None
self.planet: Optional[str] = None
self.system: Optional[str] = None
self.station: Optional[str] = None
self.station_marketid: Optional[int] = None
self.stationtype: Optional[str] = None
self.coordinates: Optional[Tuple[float, float, float]] = None
self.systemaddress: Optional[int] = None
self.systempopulation: Optional[int] = None
self.started: Optional[int] = None # Timestamp of the LoadGame event
self.mode: str | None = None
self.group: str | None = None
self.cmdr: str | None = None
self.started: int | None = None # Timestamp of the LoadGame event
self._navroute_retries_remaining = 0
self._last_navroute_journal_timestamp: Optional[float] = None
self._last_navroute_journal_timestamp: float | None = None
self._fcmaterials_retries_remaining = 0
self._last_fcmaterials_journal_timestamp: Optional[float] = None
self._last_fcmaterials_journal_timestamp: float | None = None
# For determining Live versus Legacy galaxy.
# The assumption is gameversion will parse via `coerce()` and always
@@ -144,7 +134,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
def __init_state(self) -> None:
# Cmdr state shared with EDSM and plugins
# If you change anything here update PLUGINS.md documentation!
self.state: Dict = {
self.state: dict = {
'GameLanguage': None, # From `Fileheader
'GameVersion': None, # From `Fileheader
'GameBuild': None, # From `Fileheader
@@ -195,8 +185,13 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
'Taxi': None, # True whenever we are _in_ a taxi. ie, this is reset on Disembark etc.
'Dropship': None, # Best effort as to whether or not the above taxi is a dropship.
'StarPos': None, # Best effort current system's galaxy position.
'SystemAddress': None,
'SystemName': None,
'SystemPopulation': None,
'Body': None,
'BodyID': None,
'BodyType': None,
'StationName': None,
'NavRoute': None,
}
@@ -274,7 +269,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
logger.debug('Done.')
return True
def journal_newest_filename(self, journals_dir) -> Optional[str]:
def journal_newest_filename(self, journals_dir) -> str | None:
"""
Determine the newest Journal file name.
@@ -306,19 +301,20 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
self.mode = None
self.group = None
self.cmdr = None
self.planet = None
self.system = None
self.station = None
self.station_marketid = None
self.stationtype = None
self.state['SystemAddress'] = None
self.state['SystemName'] = None
self.state['SystemPopulation'] = None
self.state['StarPos'] = None
self.state['Body'] = None
self.state['BodyID'] = None
self.state['BodyType'] = None
self.state['StationName'] = None
self.state['MarketID'] = None
self.state['StationType'] = None
self.stationservices = None
self.coordinates = None
self.systemaddress = None
self.is_beta = False
self.state['OnFoot'] = False
self.state['IsDocked'] = False
self.state['Body'] = None
self.state['BodyType'] = None
if self.observed:
logger.debug('self.observed: Calling unschedule_all()')
@@ -415,25 +411,9 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
if self.live:
if self.game_was_running:
logger.info("Game is/was running, so synthesizing StartUp event for plugins")
# Game is running locally
entry: OrderedDictT[str, Any] = OrderedDict([
('timestamp', strftime('%Y-%m-%dT%H:%M:%SZ', gmtime())),
('event', 'StartUp'),
('StarSystem', self.system),
('StarPos', self.coordinates),
('SystemAddress', self.systemaddress),
('Population', self.systempopulation),
])
if self.planet:
entry['Body'] = self.planet
entry['Docked'] = bool(self.station)
if self.station:
entry['StationName'] = self.station
entry['StationType'] = self.stationtype
entry['MarketID'] = self.station_marketid
entry = self.synthesize_startup_event()
self.event_queue.put(json.dumps(entry, separators=(', ', ':')))
@@ -455,7 +435,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
# Check whether new log file started, e.g. client (re)started.
if emitter and emitter.is_alive():
new_journal_file: Optional[str] = self.logfile # updated by on_created watchdog callback
new_journal_file: str | None = self.logfile # updated by on_created watchdog callback
else:
# Poll
@@ -534,6 +514,41 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
logger.debug('Done.')
def synthesize_startup_event(self) -> dict[str, Any]:
"""
Synthesize a 'StartUp' event to notify plugins of initial state.
May be called, e.g. after 'catch up' loading of current latest
journal file on startup, or when a new journal file is detected without
the game running locally.
:return: Synthesized event as a dict
"""
entry: dict[str, Any] = {
'timestamp': strftime('%Y-%m-%dT%H:%M:%SZ', gmtime()),
'event': 'StartUp',
'StarSystem': self.state['SystemName'],
'StarPos': self.state['StarPos'],
'SystemAddress': self.state['SystemAddress'],
'Population': self.state['SystemPopulation'],
}
if self.state['Body']:
entry['Body'] = self.state['Body']
entry['BodyID'] = self.state['BodyID']
entry['BodyType'] = self.state['BodyType']
if self.state['StationName']:
entry['Docked'] = True
entry['MarketID'] = self.state['MarketID']
entry['StationName'] = self.state['StationName']
entry['StationType'] = self.state['StationType']
else:
entry['Docked'] = False
return entry
def parse_entry(self, line: bytes) -> MutableMapping[str, Any]: # noqa: C901, CCR001
"""
Parse a Journal JSON line.
@@ -563,14 +578,16 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
self.cmdr = None
self.mode = None
self.group = None
self.planet = None
self.system = None
self.station = None
self.station_marketid = None
self.stationtype = None
self.state['SystemAddress'] = None
self.state['SystemName'] = None
self.state['SystemPopulation'] = None
self.state['StarPos'] = None
self.state['Body'] = None
self.state['BodyID'] = None
self.state['StationName'] = None
self.state['MarketID'] = None
self.state['StationType'] = None
self.stationservices = None
self.coordinates = None
self.systemaddress = None
self.started = None
self.__init_state()
@@ -599,34 +616,35 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
self.mode = entry.get('GameMode')
self.group = entry.get('Group')
self.planet = None
self.system = None
self.station = None
self.station_marketid = None
self.stationtype = None
self.state['SystemAddress'] = None
self.state['SystemName'] = None
self.state['SystemPopulation'] = None
self.state['StarPos'] = None
self.state['Body'] = None
self.state['BodyID'] = None
self.state['BodyType'] = None
self.state['StationName'] = None
self.state['MarketID'] = None
self.state['StationType'] = None
self.stationservices = None
self.coordinates = None
self.systemaddress = None
self.started = timegm(strptime(entry['timestamp'], '%Y-%m-%dT%H:%M:%SZ'))
# Don't set Ship, ShipID etc since this will reflect Fighter or SRV if starting in those
self.state.update({
'Captain': None,
'Credits': entry['Credits'],
'FID': entry.get('FID'), # From 3.3
'Horizons': entry['Horizons'], # From 3.0
'Odyssey': entry.get('Odyssey', False), # From 4.0 Odyssey
'Loan': entry['Loan'],
'Captain': None,
'Credits': entry['Credits'],
'FID': entry.get('FID'), # From 3.3
'Horizons': entry['Horizons'], # From 3.0
'Odyssey': entry.get('Odyssey', False), # From 4.0 Odyssey
'Loan': entry['Loan'],
# For Odyssey, by 4.0.0.100, and at least from Horizons 3.8.0.201 the order of events changed
# to LoadGame being after some 'status' events.
# 'Engineers': {}, # 'EngineerProgress' event now before 'LoadGame'
# 'Rank': {}, # 'Rank'/'Progress' events now before 'LoadGame'
# 'Reputation': {}, # 'Reputation' event now before 'LoadGame'
'Statistics': {}, # Still after 'LoadGame' in 4.0.0.903
'Role': None,
'Taxi': None,
'Dropship': None,
'Body': None,
'BodyType': None,
# 'Engineers': {}, # 'EngineerProgress' event now before 'LoadGame'
# 'Rank': {}, # 'Rank'/'Progress' events now before 'LoadGame'
# 'Reputation': {}, # 'Reputation' event now before 'LoadGame'
'Statistics': {}, # Still after 'LoadGame' in 4.0.0.903
'Role': None,
'Taxi': None,
'Dropship': None,
})
if entry.get('Ship') is not None and self._RE_SHIP_ONFOOT.search(entry['Ship']):
self.state['OnFoot'] = True
@@ -738,9 +756,9 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
modules.pop(from_slot, None)
elif event_type == 'undocked':
self.station = None
self.station_marketid = None
self.stationtype = None
self.state['StationName'] = None
self.state['MarketID'] = None
self.state['StationType'] = None
self.stationservices = None
self.state['IsDocked'] = False
@@ -760,9 +778,11 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
# • StationName (if at a station)
# • StationType
# • MarketID
self.station = None
self.state['StationName'] = None
self.state['MarketID'] = None
if entry.get('OnStation'):
self.station = entry.get('StationName', '')
self.state['StationName'] = entry.get('StationName', '')
self.state['MarketID'] = entry.get('MarketID', '')
self.state['OnFoot'] = False
self.state['Taxi'] = entry['Taxi']
@@ -790,10 +810,10 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
# • MarketID
if entry.get('OnStation', False):
self.station = entry.get('StationName', '')
self.state['StationName'] = entry.get('StationName', '')
else:
self.station = None
self.state['StationName'] = None
self.state['OnFoot'] = True
if self.state['Taxi'] is not None and self.state['Taxi'] != entry.get('Taxi', False):
@@ -808,77 +828,204 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
self.state['Taxi'] = False
self.state['Dropship'] = False
elif event_type == 'supercruiseexit':
# For any orbital station we have no way of determining the body
# it orbits:
#
# In-ship Status.json doesn't specify this.
# On-foot Status.json lists the station itself as Body.
# Location for stations (on-foot or in-ship) has station as Body.
# SupercruiseExit (own ship or taxi) lists the station as the Body.
if entry['BodyType'] == 'Station':
self.state['Body'] = None
self.state['BodyID'] = None
elif event_type == 'docked':
###############################################################
# Track: Station
###############################################################
self.state['IsDocked'] = True
self.station = entry.get('StationName') # May be None
self.station_marketid = entry.get('MarketID') # May be None
self.stationtype = entry.get('StationType') # May be None
self.state['StationName'] = entry.get('StationName') # It may be None
self.state['MarketID'] = entry.get('MarketID') # It may be None
self.state['StationType'] = entry.get('StationType') # It may be None
self.stationservices = entry.get('StationServices') # None under E:D < 2.4
# No need to set self.state['Taxi'] or Dropship here, if its those, the next event is a Disembark anyway
# No need to set self.state['Taxi'] or Dropship here, if it's
# those, the next event is a Disembark anyway
###############################################################
elif event_type in ('location', 'fsdjump', 'carrierjump'):
# alpha4 - any changes ?
# Location:
# New in Odyssey:
# • Taxi: bool
# • Multicrew: bool
# • InSRV: bool
# • OnFoot: bool
"""
Notes on tracking of a player's location.
Body
---
There are some caveats about tracking Body name, ID and type,
mostly due to close-orbiting binary planets/moons.
Presence on or near a Body is indicated in several scenarios:
1. When the player logs in.
2. When the player's location changes due to being docked
on a Fleet Carrier when it jumps.
3. When the player flies within Orbital Cruise range of a
Body.
For the first case this will always be a 'Location' event.
If landed on a Body, or docked at a surface port then this
will be indicated. However, if docked at an orbital station
the 'Body' is the name of that station, with 'BodyType' having
'Station' as its value.
In the second case although it *should* be a 'CarrierJump'
event, for a while now it's actually been a 'Location' event.
This should follow the same rules as being docked at an
orbital station.
For the last case there are some caveats to do with close
orbiting binary bodies:
1. 'ApproachBody' indicates presence near the Body in question.
2. 'LeaveBody' indicates the player is no longer considered
to be near the Body. This is specifically when no longer
in Orbital Cruise around the Body such that the HUD for that
has been switched out for the normal SuperCruise one.
3. 'SupercruiseExit' does not indicate any change of presence
near a Body.
4. 'SupercruiseEntry' *also* **DOES NOT** indicate that the
player is no longer near the Body. They can easily utilise
Orbital Cruise to rapidly travel around the Body and then
land on it again **without a fresh 'ApproachBody'** event.
The only way to check for this is to utilise the Body (name)
present in `Status.json` data, as this *will* correctly
reflect the second Body.
"""
###############################################################
# Track: Body
###############################################################
if event_type in ('location', 'carrierjump'):
self.planet = entry.get('Body') if entry.get('BodyType') == 'Planet' else None
# We're not guaranteeing this is a planet, rather than a
# station.
self.state['Body'] = entry.get('Body')
self.state['BodyID'] = entry.get('BodyID')
self.state['BodyType'] = entry.get('BodyType')
if event_type == 'location':
logger.trace_if('journal.locations', '"Location" event')
if entry.get('Docked'):
self.state['IsDocked'] = True
elif event_type == 'fsdjump':
self.planet = None
self.state['Body'] = None
self.state['BodyID'] = None
self.state['BodyType'] = None
###############################################################
###############################################################
# Track: IsDocked
###############################################################
if event_type == 'location':
logger.trace_if('journal.locations', '"Location" event')
self.state['IsDocked'] = entry.get('Docked', False)
###############################################################
###############################################################
# Track: Current System
###############################################################
if 'StarPos' in entry:
# Plugins need this as well, so copy in state
self.state['StarPos'] = self.coordinates = tuple(entry['StarPos']) # type: ignore
self.systemaddress = entry.get('SystemAddress')
self.systempopulation = entry.get('Population')
if entry['StarSystem'] == 'ProvingGround':
self.system = 'CQC'
self.state['StarPos'] = tuple(entry['StarPos']) # type: ignore
else:
self.system = entry['StarSystem']
logger.warning(f"'{event_type}' event without 'StarPos' !!!:\n{entry}\n")
self.station = entry.get('StationName') # May be None
# If on foot in-station 'Docked' is false, but we have a
# 'BodyType' of 'Station', and the 'Body' is the station name
# NB: No MarketID
if entry.get('BodyType') and entry['BodyType'] == 'Station':
self.station = entry.get('Body')
if 'SystemAddress' not in entry:
logger.warning(f"{event_type} event without SystemAddress !!!:\n{entry}\n")
self.station_marketid = entry.get('MarketID') # May be None
self.stationtype = entry.get('StationType') # May be None
self.stationservices = entry.get('StationServices') # None in Odyssey for on-foot 'Location'
# But we'll still *use* the value, because if a 'location' event doesn't
# have this we've still moved and now don't know where and MUST NOT
# continue to use any old value.
# Yes, explicitly state `None` here, so it's crystal clear.
self.state['SystemAddress'] = entry.get('SystemAddress', None)
self.state['SystemPopulation'] = entry.get('Population')
if entry['StarSystem'] == 'ProvingGround':
self.state['SystemName'] = 'CQC'
else:
self.state['SystemName'] = entry['StarSystem']
###############################################################
###############################################################
# Track: Current station, if applicable
###############################################################
if event_type == 'fsdjump':
self.state['StationName'] = None
self.state['MarketID'] = None
self.state['StationType'] = None
self.stationservices = None
else:
self.state['StationName'] = entry.get('StationName') # It may be None
# If on foot in-station 'Docked' is false, but we have a
# 'BodyType' of 'Station', and the 'Body' is the station name
# NB: No MarketID
if entry.get('BodyType') and entry['BodyType'] == 'Station':
self.state['StationName'] = entry.get('Body')
self.state['MarketID'] = entry.get('MarketID') # May be None
self.state['StationType'] = entry.get('StationType') # May be None
self.stationservices = entry.get('StationServices') # None in Odyssey for on-foot 'Location'
###############################################################
###############################################################
# Track: Whether in a Taxi/Dropship
###############################################################
self.state['Taxi'] = entry.get('Taxi', None)
if not self.state['Taxi']:
self.state['Dropship'] = None
###############################################################
elif event_type == 'approachbody':
self.planet = entry['Body']
self.state['Body'] = entry['Body']
self.state['BodyType'] = 'Planet' # Best guess. Journal says always planet.
self.state['BodyID'] = entry.get('BodyID')
# This isn't in the event, but Journal doc for ApproachBody says:
# when in Supercruise, and distance from planet drops to within the 'Orbital Cruise' zone
# Used in plugins/eddn.py for setting entry Body/BodyType
# on 'docked' events when Planetary.
self.state['BodyType'] = 'Planet'
elif event_type in ('leavebody', 'supercruiseentry'):
self.planet = None
elif event_type == 'leavebody':
# Triggered when ship goes above Orbital Cruise altitude, such
# that a new 'ApproachBody' would get triggered if the ship
# went back down.
self.state['Body'] = None
self.state['BodyID'] = None
self.state['BodyType'] = None
elif event_type == 'supercruiseentry':
# We only clear Body state if the Type is Station. This is
# because we won't get a fresh ApproachBody if we don't leave
# Orbital Cruise but land again.
if self.state['BodyType'] == 'Station':
self.state['Body'] = None
self.state['BodyID'] = None
self.state['BodyType'] = None
###############################################################
# Track: Current station, if applicable
###############################################################
self.state['StationName'] = None
self.state['MarketID'] = None
self.state['StationType'] = None
self.stationservices = None
###############################################################
elif event_type == 'music':
if entry['MusicTrack'] == 'MainMenu':
# We'll get new Body state when the player logs back into
# the game.
self.state['Body'] = None
self.state['BodyID'] = None
self.state['BodyType'] = None
elif event_type in ('rank', 'promotion'):
payload = dict(entry)
payload.pop('event')
@@ -1562,18 +1709,19 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
elif event_type == 'joinacrew':
self.state['Captain'] = entry['Captain']
self.state['Role'] = 'Idle'
self.planet = None
self.system = None
self.station = None
self.station_marketid = None
self.stationtype = None
self.stationservices = None
self.coordinates = None
self.systemaddress = None
self.state['OnFoot'] = False
self.state['StarPos'] = None
self.state['SystemName'] = None
self.state['SystemAddress'] = None
self.state['SystemPopulation'] = None
self.state['StarPos'] = None
self.state['Body'] = None
self.state['BodyID'] = None
self.state['BodyType'] = None
self.state['StationName'] = None
self.state['MarketID'] = None
self.state['StationType'] = None
self.stationservices = None
self.state['OnFoot'] = False
elif event_type == 'changecrewrole':
self.state['Role'] = entry['Role']
@@ -1581,17 +1729,18 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
elif event_type == 'quitacrew':
self.state['Captain'] = None
self.state['Role'] = None
self.planet = None
self.system = None
self.station = None
self.station_marketid = None
self.stationtype = None
self.stationservices = None
self.coordinates = None
self.systemaddress = None
self.state['SystemName'] = None
self.state['SystemAddress'] = None
self.state['SystemPopulation'] = None
self.state['StarPos'] = None
self.state['Body'] = None
self.state['BodyID'] = None
self.state['BodyType'] = None
self.state['StationName'] = None
self.state['MarketID'] = None
self.state['StationType'] = None
self.stationservices = None
# TODO: on_foot: Will we get an event after this to know ?
elif event_type == 'friends':
@@ -1676,11 +1825,6 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
# There should be a `Backpack` event as you 'come to' in the
# new location, so no need to zero out BackPack here.
# HACK (not game related / 2021-06-2): self.planet is moved into a more general self.state['Body'].
# This exists to help plugins doing what they SHOULDN'T BE cope. It will be removed at some point.
if self.state['Body'] is None or self.state['BodyType'] == 'Planet':
self.planet = self.state['Body']
return entry
except Exception as ex:
@@ -1918,7 +2062,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
slotid = journal_loadoutid - 4293000000
return slotid
def canonicalise(self, item: Optional[str]) -> str:
def canonicalise(self, item: str | None) -> str:
"""
Produce canonical name for a ship module.
@@ -1955,7 +2099,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
return item.capitalize()
def get_entry(self) -> Optional[MutableMapping[str, Any]]:
def get_entry(self) -> MutableMapping[str, Any] | None:
"""
Pull the next Journal event from the event_queue.
@@ -1976,34 +2120,10 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
if entry['event'] == 'Location':
logger.trace_if('journal.locations', '"Location" event')
if not self.live and entry['event'] not in (None, 'Fileheader'):
if not self.live and entry['event'] not in (None, 'Fileheader', 'ShutDown'):
# Game not running locally, but Journal has been updated
self.live = True
if self.station:
entry = OrderedDict([
('timestamp', strftime('%Y-%m-%dT%H:%M:%SZ', gmtime())),
('event', 'StartUp'),
('Docked', True),
('MarketID', self.station_marketid),
('StationName', self.station),
('StationType', self.stationtype),
('StarSystem', self.system),
('StarPos', self.coordinates),
('SystemAddress', self.systemaddress),
])
else:
entry = OrderedDict([
('timestamp', strftime('%Y-%m-%dT%H:%M:%SZ', gmtime())),
('event', 'StartUp'),
('Docked', False),
('StarSystem', self.system),
('StarPos', self.coordinates),
('SystemAddress', self.systemaddress),
])
if entry['event'] == 'Location':
logger.trace_if('journal.locations', 'Appending "Location" event to event_queue')
entry = self.synthesize_startup_event()
self.event_queue.put(json.dumps(entry, separators=(', ', ':')))
@@ -2051,7 +2171,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
return False
def ship(self, timestamped=True) -> Optional[MutableMapping[str, Any]]:
def ship(self, timestamped=True) -> MutableMapping[str, Any] | None:
"""
Produce a subset of data for the current ship.
@@ -2188,7 +2308,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
except OSError:
logger.exception("OSError writing ship loadout to new filename with utf-8 encoding, aborting.")
def coalesce_cargo(self, raw_cargo: List[MutableMapping[str, Any]]) -> List[MutableMapping[str, Any]]:
def coalesce_cargo(self, raw_cargo: list[MutableMapping[str, Any]]) -> list[MutableMapping[str, Any]]:
"""
Coalesce multiple entries of the same cargo into one.
@@ -2209,7 +2329,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
:return: Coalesced data
"""
# self.state['Cargo'].update({self.canonicalise(x['Name']): x['Count'] for x in entry['Inventory']})
out: List[MutableMapping[str, Any]] = []
out: list[MutableMapping[str, Any]] = []
for inventory_item in raw_cargo:
if not any(self.canonicalise(x['Name']) == self.canonicalise(inventory_item['Name']) for x in out):
out.append(dict(inventory_item))
@@ -2250,7 +2370,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
return slots
def _parse_navroute_file(self) -> Optional[dict[str, Any]]:
def _parse_navroute_file(self) -> dict[str, Any] | None:
"""Read and parse NavRoute.json."""
if self.currentdir is None:
raise ValueError('currentdir unset')
@@ -2276,7 +2396,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
return data
def _parse_fcmaterials_file(self) -> Optional[dict[str, Any]]:
def _parse_fcmaterials_file(self) -> dict[str, Any] | None:
"""Read and parse FCMaterials.json."""
if self.currentdir is None:
raise ValueError('currentdir unset')
@@ -2348,7 +2468,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
self._last_navroute_journal_timestamp = None
return True
def __fcmaterials_retry(self) -> Optional[Dict[str, Any]]:
def __fcmaterials_retry(self) -> dict[str, Any] | None:
"""Retry reading FCMaterials files."""
if self._fcmaterials_retries_remaining == 0:
return None
+3 -2
View File
@@ -58,7 +58,7 @@ class Notebook(ttk.Notebook):
class Frame(sys.platform == 'darwin' and tk.Frame or ttk.Frame): # type: ignore
"""Custom t(t)k.Frame class to fix some display issues."""
def __init__(self, master: Optional[ttk.Frame] = None, **kw):
def __init__(self, master: ttk.Notebook | None = None, **kw):
if sys.platform == 'darwin':
kw['background'] = kw.pop('background', PAGEBG)
tk.Frame.__init__(self, master, **kw)
@@ -76,7 +76,8 @@ class Label(tk.Label):
"""Custom tk.Label class to fix some display issues."""
def __init__(self, master: Optional[ttk.Frame] = None, **kw):
if sys.platform in ['darwin', 'win32']:
# This format chosen over `sys.platform in (...)` as mypy and friends dont understand that
if sys.platform == 'darwin' or sys.platform == 'win32':
kw['foreground'] = kw.pop('foreground', PAGEFG)
kw['background'] = kw.pop('background', PAGEBG)
else:
+2 -2
View File
@@ -93,13 +93,13 @@ def lookup(module, ship_map, entitled=False) -> Optional[dict]: # noqa: C901, C
# Countermeasures - e.g. Hpt_PlasmaPointDefence_Turret_Tiny
elif name[0] == 'hpt' and name[1] in countermeasure_map:
new['category'] = 'utility'
new['name'], new['rating'] = countermeasure_map[len(name) > 4 and (name[1], name[4]) or name[1]]
new['name'], new['rating'] = countermeasure_map[name[1]]
new['class'] = weaponclass_map[name[-1]]
# Utility - e.g. Hpt_CargoScanner_Size0_Class1
elif name[0] == 'hpt' and name[1] in utility_map:
new['category'] = 'utility'
new['name'] = utility_map[len(name) > 4 and (name[1], name[4]) or name[1]]
new['name'] = utility_map[name[1]]
if not name[2].startswith('size') or not name[3].startswith('class'):
raise AssertionError(f'{module["id"]}: Unknown class/rating "{name[2]}/{name[3]}"')
+37 -9
View File
@@ -7,7 +7,8 @@ import os
import sys
import tkinter as tk
from builtins import object, str
from typing import Any, Callable, List, Mapping, MutableMapping, Optional, Tuple
from tkinter import ttk
from typing import Any, Callable, List, Mapping, MutableMapping, Optional
import companion
import myNotebook as nb # noqa: N813
@@ -26,7 +27,7 @@ class LastError:
"""Holds the last plugin error."""
msg: Optional[str]
root: tk.Frame
root: tk.Tk
def __init__(self) -> None:
self.msg = None
@@ -118,7 +119,7 @@ class Plugin(object):
return None
def get_prefs(self, parent: tk.Frame, cmdr: str, is_beta: bool) -> Optional[tk.Frame]:
def get_prefs(self, parent: ttk.Notebook, cmdr: str | None, is_beta: bool) -> Optional[tk.Frame]:
"""
If the plugin provides a prefs frame, create and return it.
@@ -140,7 +141,7 @@ class Plugin(object):
return None
def load_plugins(master: tk.Frame) -> None: # noqa: CCR001
def load_plugins(master: tk.Tk) -> None: # noqa: CCR001
"""Find and load all plugins."""
last_error.root = master
@@ -159,7 +160,10 @@ def load_plugins(master: tk.Frame) -> None: # noqa: CCR001
sys.path.append(config.plugin_dir)
found = []
# Load any plugins that are also packages first
# Load any plugins that are also packages first, but note it's *still*
# 100% relying on there being a `load.py`, as only that will be loaded.
# The intent here is to e.g. have EDMC-Overlay load before any plugins
# that depend on it.
for name in sorted(
os.listdir(config.plugin_dir_path),
key=lambda n: (not os.path.isfile(os.path.join(config.plugin_dir_path, n, '__init__.py')), n.lower())
@@ -198,7 +202,7 @@ def provides(fn_name: str) -> List[str]:
def invoke(
plugin_name: str, fallback: str, fn_name: str, *args: Tuple
plugin_name: str, fallback: str | None, fn_name: str, *args: Any
) -> Optional[str]:
"""
Invoke a function on a named plugin.
@@ -248,7 +252,7 @@ def notify_stop() -> Optional[str]:
return error
def notify_prefs_cmdr_changed(cmdr: str, is_beta: bool) -> None:
def notify_prefs_cmdr_changed(cmdr: str | None, is_beta: bool) -> None:
"""
Notify plugins that the Cmdr was changed while the settings dialog is open.
@@ -265,7 +269,7 @@ def notify_prefs_cmdr_changed(cmdr: str, is_beta: bool) -> None:
logger.exception(f'Plugin "{plugin.name}" failed')
def notify_prefs_changed(cmdr: str, is_beta: bool) -> None:
def notify_prefs_changed(cmdr: str | None, is_beta: bool) -> None:
"""
Notify plugins that the settings dialog has been closed.
@@ -285,7 +289,7 @@ def notify_prefs_changed(cmdr: str, is_beta: bool) -> None:
def notify_journal_entry(
cmdr: str, is_beta: bool, system: str, station: str,
cmdr: str, is_beta: bool, system: str | None, station: str | None,
entry: MutableMapping[str, Any],
state: Mapping[str, Any]
) -> Optional[str]:
@@ -401,6 +405,30 @@ def notify_capidata(
return error
def notify_capi_fleetcarrierdata(
data: companion.CAPIData
) -> str | None:
"""
Send the latest CAPI Fleetcarrier data from the FD servers to each plugin.
:param data: The CAPIData returned in the CAPI response
:returns: Error message from the first plugin that returns one (if any)
"""
error = None
for plugin in PLUGINS:
fc_callback = plugin._get_func('capi_fleetcarrier')
if fc_callback is not None and callable(fc_callback):
try:
# Pass a copy of the CAPIData in case the callee modifies it
newerror = fc_callback(copy.deepcopy(data))
error = error if error else newerror
except Exception:
logger.exception(f'Plugin "{plugin.name}" failed on receiving Fleetcarrier data')
return error
def show_error(err: str) -> None:
"""
Display an error message in the status line of the main window.
+6 -6
View File
@@ -15,10 +15,9 @@
# Thus you **MUST** check if any imports you add in this file are only
# referenced in this file (or only in any other core plugin), and if so...
#
# YOU MUST ENSURE THAT PERTINENT ADJUSTMENTS ARE MADE IN `setup.py`
# SO AS TO ENSURE THE FILES ARE ACTUALLY PRESENT IN AN END-USER
# INSTALLATION ON WINDOWS.
#
# YOU MUST ENSURE THAT PERTINENT ADJUSTMENTS ARE MADE IN
# `Build-exe-and-msi.py` SO AS TO ENSURE THE FILES ARE ACTUALLY PRESENT
# IN AN END-USER INSTALLATION ON WINDOWS.
#
# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $#
# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $#
@@ -27,6 +26,7 @@ import gzip
import io
import json
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING, Union
import myNotebook as nb # noqa: N813 # its not my fault.
@@ -80,7 +80,7 @@ def plugin_start3(path: str) -> str:
return 'Coriolis'
def plugin_prefs(parent: tk.Widget, cmdr: str, is_beta: bool) -> tk.Frame:
def plugin_prefs(parent: ttk.Notebook, cmdr: str | None, is_beta: bool) -> tk.Frame:
"""Set up plugin preferences."""
PADX = 10 # noqa: N806
@@ -127,7 +127,7 @@ def plugin_prefs(parent: tk.Widget, cmdr: str, is_beta: bool) -> tk.Frame:
return conf_frame
def prefs_changed(cmdr: str, is_beta: bool) -> None:
def prefs_changed(cmdr: str | None, is_beta: bool) -> None:
"""Update URLs."""
global normal_url, beta_url, override_mode
normal_url = normal_textvar.get()
+40 -59
View File
@@ -42,7 +42,7 @@
# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $#
# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $#
import tkinter
from typing import TYPE_CHECKING, Any, Mapping, MutableMapping, Optional
from typing import TYPE_CHECKING, Any, Mapping
import requests
@@ -50,7 +50,7 @@ import EDMCLogging
import killswitch
import plug
from companion import CAPIData
from config import config
from config import appname, config
if TYPE_CHECKING:
from tkinter import Tk
@@ -69,12 +69,12 @@ class This:
def __init__(self) -> None:
# Main window clicks
self.system_link: tkinter.Widget
self.system: Optional[str] = None
self.system_address: Optional[str] = None
self.system_population: Optional[int] = None
self.system_name: str | None = None
self.system_address: str | None = None
self.system_population: int | None = None
self.station_link: tkinter.Widget
self.station: Optional[str] = None
self.station_marketid: Optional[int] = None
self.station_name: str | None = None
self.station_marketid: int | None = None
self.on_foot = False
@@ -131,12 +131,14 @@ def plugin_app(parent: 'Tk'):
:param parent: The tk parent to place our widgets into.
:return: See PLUGINS.md#display
"""
this.system_link = parent.children['system'] # system label in main window
this.system = None
# system label in main window
this.system_link = parent.nametowidget(f".{appname.lower()}.system")
this.system_name = None
this.system_address = None
this.station = None
this.station_name = None
this.station_marketid = None # Frontier MarketID
this.station_link = parent.children['station'] # station label in main window
# station label in main window
this.station_link = parent.nametowidget(f".{appname.lower()}.station")
this.station_link['popup_copy'] = lambda x: x != this.STATION_UNDOCKED
@@ -152,9 +154,9 @@ def prefs_changed(cmdr: str, is_beta: bool) -> None:
pass
def journal_entry( # noqa: CCR001
def journal_entry(
cmdr: str, is_beta: bool, system: str, station: str,
entry: MutableMapping[str, Any],
entry: dict[str, Any],
state: Mapping[str, Any]
):
"""
@@ -168,6 +170,9 @@ def journal_entry( # noqa: CCR001
:param state: `monitor.state`
:return: None if no error, else an error string.
"""
should_return: bool
new_entry: dict[str, Any] = {}
should_return, new_entry = killswitch.check_killswitch('plugins.eddb.journal', entry)
if should_return:
# LANG: Journal Processing disabled due to an active killswitch
@@ -179,59 +184,35 @@ def journal_entry( # noqa: CCR001
return
this.on_foot = state['OnFoot']
# Always update our system address even if we're not currently the provider for system or station, but dont update
# on events that contain "future" data, such as FSDTarget
if entry['event'] in ('Location', 'Docked', 'CarrierJump', 'FSDJump'):
this.system_address = entry.get('SystemAddress') or this.system_address
this.system = entry.get('StarSystem') or this.system
this.system_address = state['SystemAddress']
this.system_name = state['SystemName']
this.system_population = state['SystemPopulation']
this.station_name = state['StationName']
this.station_marketid = state['MarketID']
# We need pop == 0 to set the value so as to clear 'x' in systems with
# no stations.
pop = entry.get('Population')
if pop is not None:
this.system_population = pop
this.station = entry.get('StationName') or this.station
# on_foot station detection
if entry['event'] == 'Location' and entry['BodyType'] == 'Station':
this.station = entry['Body']
this.station_marketid = entry.get('MarketID') or this.station_marketid
# We might pick up StationName in DockingRequested, make sure we clear it if leaving
if entry['event'] in ('Undocked', 'FSDJump', 'SupercruiseEntry'):
this.station = None
this.station_marketid = None
if entry['event'] == 'Embark' and not entry.get('OnStation'):
# If we're embarking OnStation to a Taxi/Dropship we'll also get an
# Undocked event.
this.station = None
this.station_marketid = None
# Only actually change URLs if we are current provider.
if config.get_str('system_provider') == 'eddb':
this.system_link['text'] = this.system
# Only change URL text if we are current provider.
if config.get_str('station_provider') == 'eddb':
this.system_link['text'] = this.system_name
# Do *NOT* set 'url' here, as it's set to a function that will call
# through correctly. We don't want a static string.
this.system_link.update_idletasks()
# But only actually change the URL if we are current station provider.
if config.get_str('station_provider') == 'eddb':
text = this.station
if not text:
if this.station_name:
this.station_link['text'] = this.station_name
else:
if this.system_population is not None and this.system_population > 0:
text = this.STATION_UNDOCKED
this.station_link['text'] = this.STATION_UNDOCKED
else:
text = ''
this.station_link['text'] = ''
this.station_link['text'] = text
# Do *NOT* set 'url' here, as it's set to a function that will call
# through correctly. We don't want a static string.
this.station_link.update_idletasks()
def cmdr_data(data: CAPIData, is_beta: bool) -> Optional[str]:
def cmdr_data(data: CAPIData, is_beta: bool) -> str | None:
"""
Process new CAPI data.
@@ -244,22 +225,22 @@ def cmdr_data(data: CAPIData, is_beta: bool) -> Optional[str]:
this.station_marketid = data['lastStarport']['id']
# Only trust CAPI if these aren't yet set
if not this.system:
this.system = data['lastSystem']['name']
if not this.system_name:
this.system_name = data['lastSystem']['name']
if not this.station and data['commander']['docked']:
this.station = data['lastStarport']['name']
if not this.station_name and data['commander']['docked']:
this.station_name = data['lastStarport']['name']
# Override standard URL functions
if config.get_str('system_provider') == 'eddb':
this.system_link['text'] = this.system
this.system_link['text'] = this.system_name
# Do *NOT* set 'url' here, as it's set to a function that will call
# through correctly. We don't want a static string.
this.system_link.update_idletasks()
if config.get_str('station_provider') == 'eddb':
if data['commander']['docked'] or this.on_foot and this.station:
this.station_link['text'] = this.station
if data['commander']['docked'] or this.on_foot and this.station_name:
this.station_link['text'] = this.station_name
elif data['lastStarport']['name'] and data['lastStarport']['name'] != "":
this.station_link['text'] = this.STATION_UNDOCKED
+195 -110
View File
@@ -15,10 +15,9 @@
# Thus you **MUST** check if any imports you add in this file are only
# referenced in this file (or only in any other core plugin), and if so...
#
# YOU MUST ENSURE THAT PERTINENT ADJUSTMENTS ARE MADE IN `setup.py`
# SO AS TO ENSURE THE FILES ARE ACTUALLY PRESENT IN AN END-USER
# INSTALLATION ON WINDOWS.
#
# YOU MUST ENSURE THAT PERTINENT ADJUSTMENTS ARE MADE IN
# `Build-exe-and-msi.py` SO AS TO ENSURE THE FILES ARE ACTUALLY PRESENT
# IN AN END-USER INSTALLATION ON WINDOWS.
#
# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $#
# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $#
@@ -35,7 +34,7 @@ from collections import OrderedDict
from platform import system
from textwrap import dedent
from threading import Lock
from typing import TYPE_CHECKING, Any, Dict, Iterator, List, Mapping, MutableMapping, Optional
from typing import TYPE_CHECKING, Any, Iterator, Mapping, MutableMapping, Optional
from typing import OrderedDict as OrderedDictT
from typing import Tuple, Union
@@ -47,7 +46,7 @@ import killswitch
import myNotebook as nb # noqa: N813
import plug
from companion import CAPIData, category_map
from config import applongname, appversion_nobuild, config, debug_senders, user_agent
from config import applongname, appname, appversion_nobuild, config, debug_senders, user_agent
from EDMCLogging import get_main_logger
from monitor import monitor
from myNotebook import Frame
@@ -83,22 +82,27 @@ class This:
self.odyssey = False
# Track location to add to Journal events
self.systemaddress: Optional[str] = None
self.system_address: Optional[str] = None
self.system_name: Optional[str] = None
self.coordinates: Optional[Tuple] = None
self.body_name: Optional[str] = None
self.body_id: Optional[int] = None
self.body_type: Optional[int] = None
self.station_name: str | None = None
self.station_type: str | None = None
self.station_marketid: str | None = None
# Track Status.json data
self.status_body_name: Optional[str] = None
# Avoid duplicates
self.marketId: Optional[str] = None
self.commodities: Optional[List[OrderedDictT[str, Any]]] = None
self.outfitting: Optional[Tuple[bool, List[str]]] = None
self.shipyard: Optional[Tuple[bool, List[Mapping[str, Any]]]] = None
self.commodities: Optional[list[OrderedDictT[str, Any]]] = None
self.outfitting: Optional[Tuple[bool, list[str]]] = None
self.shipyard: Optional[Tuple[bool, list[Mapping[str, Any]]]] = None
self.fcmaterials_marketid: int = 0
self.fcmaterials: Optional[List[OrderedDictT[str, Any]]] = None
self.fcmaterials: Optional[list[OrderedDictT[str, Any]]] = None
self.fcmaterials_capi_marketid: int = 0
self.fcmaterials_capi: Optional[List[OrderedDictT[str, Any]]] = None
self.fcmaterials_capi: Optional[list[OrderedDictT[str, Any]]] = None
# For the tkinter parent window, so we can call update_idletasks()
self.parent: tk.Tk
@@ -118,9 +122,15 @@ class This:
# Tracking UI
self.ui: tk.Frame
self.ui_system_name: tk.Label
self.ui_system_address: tk.Label
self.ui_j_body_name: tk.Label
self.ui_j_body_id: tk.Label
self.ui_j_body_type: tk.Label
self.ui_s_body_name: tk.Label
self.ui_station_name: tk.Label
self.ui_station_type: tk.Label
self.ui_station_marketid: tk.Label
this = This()
@@ -380,10 +390,10 @@ class EDDNSender:
:param text: The status text to be set/logged.
"""
if os.getenv('EDMC_NO_UI'):
logger.INFO(text)
logger.info(text)
return
self.eddn.parent.children['status']['text'] = text
self.eddn.parent.nametowidget(f".{appname.lower()}.status")['text'] = text
def send_message(self, msg: str) -> bool:
"""
@@ -403,6 +413,9 @@ class EDDNSender:
:return: `True` for "now remove this message from the queue"
"""
logger.trace_if("plugin.eddn.send", "Sending message")
should_return: bool
new_data: dict[str, Any]
should_return, new_data = killswitch.check_killswitch('plugins.eddn.send', json.loads(msg))
if should_return:
logger.warning('eddn.send has been disabled via killswitch. Returning.')
@@ -613,7 +626,7 @@ class EDDN:
self.sender = EDDNSender(self, self.eddn_url)
self.fss_signals: List[Mapping[str, Any]] = []
self.fss_signals: list[Mapping[str, Any]] = []
def close(self):
"""Close down the EDDN class instance."""
@@ -636,13 +649,25 @@ class EDDN:
:param data: a dict containing the starport data
:param is_beta: whether or not we're currently in beta mode
"""
should_return: bool
new_data: dict[str, Any]
should_return, new_data = killswitch.check_killswitch('capi.request./market', {})
if should_return:
logger.warning("capi.request./market has been disabled by killswitch. Returning.")
return
should_return, new_data = killswitch.check_killswitch('eddn.capi_export.commodities', {})
if should_return:
logger.warning("eddn.capi_export.commodities has been disabled by killswitch. Returning.")
return
modules, ships = self.safe_modules_and_ships(data)
horizons: bool = capi_is_horizons(
data['lastStarport'].get('economies', {}),
modules,
ships
)
commodities: List[OrderedDictT[str, Any]] = []
commodities: list[OrderedDictT[str, Any]] = []
for commodity in data['lastStarport'].get('commodities') or []:
# Check 'marketable' and 'not prohibited'
if (category_map.get(commodity['categoryname'], True)
@@ -704,7 +729,7 @@ class EDDN:
# Send any FCMaterials.json-equivalent 'orders' as well
self.export_capi_fcmaterials(data, is_beta, horizons)
def safe_modules_and_ships(self, data: Mapping[str, Any]) -> Tuple[Dict, Dict]:
def safe_modules_and_ships(self, data: Mapping[str, Any]) -> Tuple[dict, dict]:
"""
Produce a sanity-checked version of ships and modules from CAPI data.
@@ -715,7 +740,7 @@ class EDDN:
:param data: The raw CAPI data.
:return: Sanity-checked data.
"""
modules: Dict[str, Any] = data['lastStarport'].get('modules')
modules: dict[str, Any] = data['lastStarport'].get('modules')
if modules is None or not isinstance(modules, dict):
if modules is None:
logger.debug('modules was None. FC or Damaged Station?')
@@ -732,7 +757,7 @@ class EDDN:
# Set a safe value
modules = {}
ships: Dict[str, Any] = data['lastStarport'].get('ships')
ships: dict[str, Any] = data['lastStarport'].get('ships')
if ships is None or not isinstance(ships, dict):
if ships is None:
logger.debug('ships was None')
@@ -757,6 +782,18 @@ class EDDN:
:param data: dict containing the outfitting data
:param is_beta: whether or not we're currently in beta mode
"""
should_return: bool
new_data: dict[str, Any]
should_return, new_data = killswitch.check_killswitch('capi.request./shipyard', {})
if should_return:
logger.warning("capi.request./shipyard has been disabled by killswitch. Returning.")
return
should_return, new_data = killswitch.check_killswitch('eddn.capi_export.outfitting', {})
if should_return:
logger.warning("eddn.capi_export.outfitting has been disabled by killswitch. Returning.")
return
modules, ships = self.safe_modules_and_ships(data)
# Horizons flag - will hit at least Int_PlanetApproachSuite other than at engineer bases ("Colony"),
@@ -773,7 +810,7 @@ class EDDN:
modules.values()
)
outfitting: List[str] = sorted(
outfitting: list[str] = sorted(
self.MODULE_RE.sub(lambda match: match.group(0).capitalize(), mod['name'].lower()) for mod in to_search
)
@@ -813,6 +850,18 @@ class EDDN:
:param data: dict containing the shipyard data
:param is_beta: whether or not we are in beta mode
"""
should_return: bool
new_data: dict[str, Any]
should_return, new_data = killswitch.check_killswitch('capi.request./shipyard', {})
if should_return:
logger.warning("capi.request./shipyard has been disabled by killswitch. Returning.")
return
should_return, new_data = killswitch.check_killswitch('eddn.capi_export.shipyard', {})
if should_return:
logger.warning("eddn.capi_export.shipyard has been disabled by killswitch. Returning.")
return
modules, ships = self.safe_modules_and_ships(data)
horizons: bool = capi_is_horizons(
@@ -821,7 +870,7 @@ class EDDN:
ships
)
shipyard: List[Mapping[str, Any]] = sorted(
shipyard: list[Mapping[str, Any]] = sorted(
itertools.chain(
(ship['name'].lower() for ship in (ships['shipyard_list'] or {}).values()),
(ship['name'].lower() for ship in ships['unavailable_list'] or {}),
@@ -864,8 +913,8 @@ class EDDN:
:param is_beta: whether or not we're in beta mode
:param entry: the journal entry containing the commodities data
"""
items: List[Mapping[str, Any]] = entry.get('Items') or []
commodities: List[OrderedDictT[str, Any]] = sorted((OrderedDict([
items: list[Mapping[str, Any]] = entry.get('Items') or []
commodities: list[OrderedDictT[str, Any]] = sorted((OrderedDict([
('name', self.canonicalise(commodity['Name'])),
('meanPrice', commodity['MeanPrice']),
('buyPrice', commodity['BuyPrice']),
@@ -912,11 +961,11 @@ class EDDN:
:param is_beta: Whether or not we're in beta mode
:param entry: The relevant journal entry
"""
modules: List[Mapping[str, Any]] = entry.get('Items', [])
modules: list[Mapping[str, Any]] = entry.get('Items', [])
horizons: bool = entry.get('Horizons', False)
# outfitting = sorted([self.MODULE_RE.sub(lambda m: m.group(0).capitalize(), module['Name'])
# for module in modules if module['Name'] != 'int_planetapproachsuite'])
outfitting: List[str] = sorted(
outfitting: list[str] = sorted(
self.MODULE_RE.sub(lambda m: m.group(0).capitalize(), mod['Name']) for mod in
filter(lambda m: m['Name'] != 'int_planetapproachsuite', modules)
)
@@ -951,7 +1000,7 @@ class EDDN:
:param is_beta: Whether or not we're in beta mode
:param entry: the relevant journal entry
"""
ships: List[Mapping[str, Any]] = entry.get('PriceList') or []
ships: list[Mapping[str, Any]] = entry.get('PriceList') or []
horizons: bool = entry.get('Horizons', False)
shipyard = sorted(ship['ShipType'] for ship in ships)
# Don't send empty ships list - shipyard data is only guaranteed present if user has visited the shipyard.
@@ -1078,18 +1127,17 @@ class EDDN:
entry['StarSystem'] = system_name
if 'SystemAddress' not in entry:
if this.systemaddress is None:
if this.system_address is None:
logger.warning("this.systemaddress is None, can't add SystemAddress")
return "this.systemaddress is None, can't add SystemAddress"
entry['SystemAddress'] = this.systemaddress
entry['SystemAddress'] = this.system_address
if 'StarPos' not in entry:
# Prefer the passed-in, probably monitor.state version
# Prefer the passed-in version
if system_coordinates is not None:
entry['StarPos'] = system_coordinates
# TODO: Deprecate in-plugin tracking
elif this.coordinates is not None:
entry['StarPos'] = list(this.coordinates)
@@ -1122,7 +1170,7 @@ class EDDN:
#######################################################################
# In this case should add StarPos, but only if the
# SystemAddress of where we think we are matches.
if this.systemaddress is None or this.systemaddress != entry['SystemAddress']:
if this.system_address is None or this.system_address != entry['SystemAddress']:
logger.warning("SystemAddress isn't current location! Can't add augmentations!")
return 'Wrong System! Missed jump ?'
@@ -1164,7 +1212,7 @@ class EDDN:
#######################################################################
# In this case should add StarSystem and StarPos, but only if the
# SystemAddress of where we think we are matches.
if this.systemaddress is None or this.systemaddress != entry['SystemAddress']:
if this.system_address is None or this.system_address != entry['SystemAddress']:
logger.warning("SystemAddress isn't current location! Can't add augmentations!")
return 'Wrong System! Missed jump ?'
@@ -1226,7 +1274,7 @@ class EDDN:
#######################################################################
# In this case should add StarPos, but only if the
# SystemAddress of where we think we are matches.
if this.systemaddress is None or this.systemaddress != entry['SystemAddress']:
if this.system_address is None or this.system_address != entry['SystemAddress']:
logger.warning("SystemAddress isn't current location! Can't add augmentations!")
return 'Wrong System! Missed jump ?'
@@ -1241,6 +1289,8 @@ class EDDN:
logger.warning(f'this.status_body_name was not set properly:'
f' "{this.status_body_name}" ({type(this.status_body_name)})')
# this.status_body_name is available for cross-checks, so try to set
# BodyName and ID.
else:
# In case Frontier add it in
if 'BodyName' not in entry:
@@ -1318,7 +1368,7 @@ class EDDN:
#######################################################################
# In this case should add StarPos, but only if the
# SystemAddress of where we think we are matches.
if this.systemaddress is None or this.systemaddress != entry['SystemAddress']:
if this.system_address is None or this.system_address != entry['SystemAddress']:
logger.warning("SystemAddress isn't current location! Can't add augmentations!")
return 'Wrong System! Missed jump ?'
@@ -1613,7 +1663,7 @@ class EDDN:
#######################################################################
# In this case should add SystemName and StarPos, but only if the
# SystemAddress of where we think we are matches.
if this.systemaddress is None or this.systemaddress != entry['SystemAddress']:
if this.system_address is None or this.system_address != entry['SystemAddress']:
logger.warning("SystemAddress isn't current location! Can't add augmentations!")
return 'Wrong System! Missed jump ?'
@@ -1663,7 +1713,7 @@ class EDDN:
#######################################################################
# In this case should add StarPos, but only if the
# SystemAddress of where we think we are matches.
if this.systemaddress is None or this.systemaddress != entry['SystemAddress']:
if this.system_address is None or this.system_address != entry['SystemAddress']:
logger.warning("SystemAddress isn't current location! Can't add augmentations!")
return 'Wrong System! Missed jump ?'
@@ -1719,7 +1769,7 @@ class EDDN:
#######################################################################
# In this case should add SystemName and StarPos, but only if the
# SystemAddress of where we think we are matches.
if this.systemaddress is None or this.systemaddress != entry['SystemAddress']:
if this.system_address is None or this.system_address != entry['SystemAddress']:
logger.warning("SystemAddress isn't current location! Can't add augmentations!")
return 'Wrong System! Missed jump ?'
@@ -1777,11 +1827,11 @@ class EDDN:
else:
# Horizons order, so use tracked data for cross-check
if this.systemaddress is None or system_name is None or system_starpos is None:
logger.error(f'Location tracking failure: {this.systemaddress=}, {system_name=}, {system_starpos=}')
if this.system_address is None or system_name is None or system_starpos is None:
logger.error(f'Location tracking failure: {this.system_address=}, {system_name=}, {system_starpos=}')
return 'Current location not tracked properly, started after game?'
aug_systemaddress = this.systemaddress
aug_systemaddress = this.system_address
aug_starsystem = system_name
aug_starpos = system_starpos
@@ -1793,7 +1843,7 @@ class EDDN:
#######################################################################
# Build basis of message
msg: Dict = {
msg: dict = {
'$schemaRef': f'https://eddn.edcd.io/schemas/fsssignaldiscovered/1{"/test" if is_beta else ""}',
'message': {
"event": "FSSSignalDiscovered",
@@ -1857,7 +1907,7 @@ class EDDN:
match = self.CANONICALISE_RE.match(item)
return match and match.group(1) or item
def capi_gameversion_from_host_endpoint(self, capi_host: str, capi_endpoint: str) -> str:
def capi_gameversion_from_host_endpoint(self, capi_host: Optional[str], capi_endpoint: str) -> str:
"""
Return the correct CAPI gameversion string for the given host/endpoint.
@@ -1925,23 +1975,75 @@ def plugin_app(parent: tk.Tk) -> Optional[tk.Frame]:
this.ui = tk.Frame(parent)
row = this.ui.grid_size()[1]
#######################################################################
# System
#######################################################################
# SystemName
system_name_label = tk.Label(this.ui, text="J:SystemName:")
system_name_label.grid(row=row, column=0, sticky=tk.W)
this.ui_system_name = tk.Label(this.ui, name='eddn_track_system_name', anchor=tk.W)
this.ui_system_name.grid(row=row, column=1, sticky=tk.E)
row += 1
# SystemAddress
system_address_label = tk.Label(this.ui, text="J:SystemAddress:")
system_address_label.grid(row=row, column=0, sticky=tk.W)
this.ui_system_address = tk.Label(this.ui, name='eddn_track_system_address', anchor=tk.W)
this.ui_system_address.grid(row=row, column=1, sticky=tk.E)
row += 1
#######################################################################
#######################################################################
# Body
#######################################################################
# Body Name from Journal
journal_body_name_label = tk.Label(this.ui, text="J:BodyName:")
journal_body_name_label.grid(row=row, column=0, sticky=tk.W)
this.ui_j_body_name = tk.Label(this.ui, name='eddn_track_j_body_name', anchor=tk.W)
this.ui_j_body_name.grid(row=row, column=1, sticky=tk.E)
row += 1
# Body ID from Journal
journal_body_id_label = tk.Label(this.ui, text="J:BodyID:")
journal_body_id_label.grid(row=row, column=0, sticky=tk.W)
this.ui_j_body_id = tk.Label(this.ui, name='eddn_track_j_body_id', anchor=tk.W)
this.ui_j_body_id.grid(row=row, column=1, sticky=tk.E)
row += 1
# Body Type from Journal
journal_body_type_label = tk.Label(this.ui, text="J:BodyType:")
journal_body_type_label.grid(row=row, column=0, sticky=tk.W)
this.ui_j_body_type = tk.Label(this.ui, name='eddn_track_j_body_type', anchor=tk.W)
this.ui_j_body_type.grid(row=row, column=1, sticky=tk.E)
row += 1
# Body Name from Status.json
status_body_name_label = tk.Label(this.ui, text="S:BodyName:")
status_body_name_label.grid(row=row, column=0, sticky=tk.W)
this.ui_s_body_name = tk.Label(this.ui, name='eddn_track_s_body_name', anchor=tk.W)
this.ui_s_body_name.grid(row=row, column=1, sticky=tk.E)
row += 1
#######################################################################
#######################################################################
# Station
#######################################################################
# Name
status_station_name_label = tk.Label(this.ui, text="J:StationName:")
status_station_name_label.grid(row=row, column=0, sticky=tk.W)
this.ui_station_name = tk.Label(this.ui, name='eddn_track_station_name', anchor=tk.W)
this.ui_station_name.grid(row=row, column=1, sticky=tk.E)
row += 1
# Type
status_station_type_label = tk.Label(this.ui, text="J:StationType:")
status_station_type_label.grid(row=row, column=0, sticky=tk.W)
this.ui_station_type = tk.Label(this.ui, name='eddn_track_station_type', anchor=tk.W)
this.ui_station_type.grid(row=row, column=1, sticky=tk.E)
row += 1
# MarketID
status_station_marketid_label = tk.Label(this.ui, text="J:StationID:")
status_station_marketid_label.grid(row=row, column=0, sticky=tk.W)
this.ui_station_marketid = tk.Label(this.ui, name='eddn_track_station_id', anchor=tk.W)
this.ui_station_marketid.grid(row=row, column=1, sticky=tk.E)
row += 1
#######################################################################
return this.ui
@@ -1953,6 +2055,14 @@ def tracking_ui_update() -> None:
if not config.eddn_tracking_ui:
return
this.ui_system_name['text'] = '≪None≫'
if this.ui_system_name is not None:
this.ui_system_name['text'] = this.system_name
this.ui_system_address['text'] = '≪None≫'
if this.ui_system_address is not None:
this.ui_system_address['text'] = this.system_address
this.ui_j_body_name['text'] = '≪None≫'
if this.body_name is not None:
this.ui_j_body_name['text'] = this.body_name
@@ -1961,10 +2071,26 @@ def tracking_ui_update() -> None:
if this.body_id is not None:
this.ui_j_body_id['text'] = str(this.body_id)
this.ui_j_body_type['text'] = '≪None≫'
if this.body_type is not None:
this.ui_j_body_type['text'] = str(this.body_type)
this.ui_s_body_name['text'] = '≪None≫'
if this.status_body_name is not None:
this.ui_s_body_name['text'] = this.status_body_name
this.ui_station_name['text'] = '≪None≫'
if this.station_name is not None:
this.ui_station_name['text'] = this.station_name
this.ui_station_type['text'] = '≪None≫'
if this.station_type is not None:
this.ui_station_type['text'] = this.station_type
this.ui_station_marketid['text'] = '≪None≫'
if this.station_marketid is not None:
this.ui_station_marketid['text'] = this.station_marketid
this.ui.update_idletasks()
@@ -2170,68 +2296,28 @@ def journal_entry( # noqa: C901, CCR001
entry
)
# Track location
if event_name == 'supercruiseexit':
# For any orbital station we have no way of determining the body
# it orbits:
#
# In-ship Status.json doesn't specify this.
# On-foot Status.json lists the station itself as Body.
# Location for stations (on-foot or in-ship) has station as Body.
# SupercruiseExit (own ship or taxi) lists the station as the Body.
if entry['BodyType'] == 'Station':
this.body_name = None
this.body_id = None
# Copy some state into module-held variables because we might need it
# outside of this function.
this.body_name = state['Body']
this.body_id = state['BodyID']
this.body_type = state['BodyType']
this.coordinates = state['StarPos']
this.system_address = state['SystemAddress']
this.system_name = state['SystemName']
this.station_name = state['StationName']
this.station_type = state['StationType']
this.station_marketid = state['MarketID']
elif event_name in ('location', 'fsdjump', 'docked', 'carrierjump'):
if event_name in ('location', 'carrierjump'):
if entry.get('BodyType') == 'Planet':
this.body_name = entry.get('Body')
this.body_id = entry.get('BodyID')
else:
this.body_name = None
elif event_name == 'fsdjump':
this.body_name = None
this.body_id = None
if 'StarPos' in entry:
this.coordinates = tuple(entry['StarPos'])
elif this.systemaddress != entry.get('SystemAddress'):
this.coordinates = None # Docked event doesn't include coordinates
if 'SystemAddress' not in entry:
logger.warning(f'"location" event without SystemAddress !!!:\n{entry}\n')
# But we'll still *use* the value, because if a 'location' event doesn't
# have this we've still moved and now don't know where and MUST NOT
# continue to use any old value.
# Yes, explicitly state `None` here, so it's crystal clear.
this.systemaddress = entry.get('SystemAddress', None) # type: ignore
if event_name == 'docked':
this.eddn.parent.after(this.eddn.REPLAY_DELAY, this.eddn.sender.queue_check_and_send, False)
elif event_name == 'approachbody':
this.body_name = entry['Body']
this.body_id = entry.get('BodyID')
elif event_name == 'leavebody':
# NB: **NOT** SupercruiseEntry, because we won't get a fresh
# ApproachBody if we don't leave Orbital Cruise and land again.
# *This* is triggered when you go above Orbital Cruise altitude.
# Status.json BodyName clears when the OC/Glide HUD is deactivated.
this.body_name = None
this.body_id = None
if event_name == 'docked':
# Trigger a send/retry of pending EDDN messages
this.eddn.parent.after(this.eddn.REPLAY_DELAY, this.eddn.sender.queue_check_and_send, False)
elif event_name == 'music':
if entry['MusicTrack'] == 'MainMenu':
this.body_name = None
this.body_id = None
this.status_body_name = None
tracking_ui_update()
# Events with their own EDDN schema
if config.get_int('output') & config.OUT_EDDN_SEND_NON_STATION and not state['Captain']:
@@ -2321,9 +2407,10 @@ def journal_entry( # noqa: C901, CCR001
]
# add planet to Docked event for planetary stations if known
if event_name == 'docked' and this.body_name:
entry['Body'] = this.body_name
entry['BodyType'] = 'Planet'
if event_name == 'docked' and state['Body'] is not None:
if state['BodyType'] == 'Planet':
entry['Body'] = state['Body']
entry['BodyType'] = state['BodyType']
# The generic journal schema is for events:
# Docked, FSDJump, Scan, Location, SAASignalsFound, CarrierJump
@@ -2344,7 +2431,7 @@ def journal_entry( # noqa: C901, CCR001
# add mandatory StarSystem and StarPos properties to events
if 'StarSystem' not in entry:
if this.systemaddress is None or this.systemaddress != entry['SystemAddress']:
if this.system_address is None or this.system_address != entry['SystemAddress']:
logger.warning(f"event({entry['event']}) has no StarSystem, but SystemAddress isn't current location")
return "Wrong System! Delayed Scan event?"
@@ -2361,7 +2448,7 @@ def journal_entry( # noqa: C901, CCR001
# Gazelle[TD] reported seeing a lagged Scan event with incorrect
# augmented StarPos: <https://github.com/EDCD/EDMarketConnector/issues/961>
if this.systemaddress is None or this.systemaddress != entry['SystemAddress']:
if this.system_address is None or this.system_address != entry['SystemAddress']:
logger.warning(f"event({entry['event']}) has no StarPos, but SystemAddress isn't current location")
return "Wrong System! Delayed Scan event?"
@@ -2416,8 +2503,6 @@ def journal_entry( # noqa: C901, CCR001
logger.debug(f'Failed exporting {entry["event"]}', exc_info=e)
return str(e)
tracking_ui_update()
return None
@@ -2456,14 +2541,14 @@ def cmdr_data(data: CAPIData, is_beta: bool) -> Optional[str]: # noqa: CCR001
):
this.cmdr_name = cmdr_name
if (data['commander'].get('docked') or (this.on_foot and monitor.station)
if (data['commander'].get('docked') or (this.on_foot and monitor.state['StationName'])
and config.get_int('output') & config.OUT_EDDN_SEND_STATION_DATA):
try:
if this.marketId != data['lastStarport']['id']:
this.commodities = this.outfitting = this.shipyard = None
this.marketId = data['lastStarport']['id']
status = this.parent.children['status']
status = this.parent.nametowidget(f".{appname.lower()}.status")
old_status = status['text']
if not old_status:
status['text'] = _('Sending data to EDDN...') # LANG: Status text shown while attempting to send data
@@ -2541,7 +2626,7 @@ def capi_is_horizons(economies: MAP_STR_ANY, modules: MAP_STR_ANY, ships: MAP_ST
return economies_colony or modules_horizons or ship_horizons
def dashboard_entry(cmdr: str, is_beta: bool, entry: Dict[str, Any]) -> None:
def dashboard_entry(cmdr: str, is_beta: bool, entry: dict[str, Any]) -> None:
"""
Process Status.json data to track things like current Body.
+166 -148
View File
@@ -38,16 +38,18 @@ from datetime import datetime, timedelta, timezone
from queue import Queue
from threading import Thread
from time import sleep
from typing import TYPE_CHECKING, Any, List, Literal, Mapping, MutableMapping, Optional, Set, Tuple, Union, cast
from tkinter import ttk
from typing import TYPE_CHECKING, Any, Dict, List, Literal, Mapping, MutableMapping, Optional, Set, Tuple, Union, cast
import requests
import killswitch
import monitor
import myNotebook
import myNotebook as nb # noqa: N813
import plug
from companion import CAPIData
from config import applongname, appversion, config, debug_senders, user_agent
from config import applongname, appname, appversion, config, debug_senders, user_agent
from edmc_data import DEBUG_WEBSERVER_HOST, DEBUG_WEBSERVER_PORT
from EDMCLogging import get_main_logger
from ttkHyperlinkLabel import HyperlinkLabel
@@ -82,8 +84,8 @@ class This:
self.session: requests.Session = requests.Session()
self.session.headers['User-Agent'] = user_agent
self.queue: Queue = Queue() # Items to be sent to EDSM by worker thread
self.discarded_events: Set[str] = [] # List discarded events from EDSM
self.lastlookup: requests.Response # Result of last system lookup
self.discarded_events: Set[str] = set() # List discarded events from EDSM
self.lastlookup: Dict[str, Any] # Result of last system lookup
# Game state
self.multicrew: bool = False # don't send captain's ship info to EDSM while on a crew
@@ -91,13 +93,13 @@ class This:
self.newgame: bool = False # starting up - batch initial burst of events
self.newgame_docked: bool = False # starting up while docked
self.navbeaconscan: int = 0 # batch up burst of Scan events after NavBeaconScan
self.system_link: tk.Widget = None
self.system: tk.Tk = None
self.system_address: Optional[int] = None # Frontier SystemAddress
self.system_population: Optional[int] = None
self.station_link: tk.Widget = None
self.station: Optional[str] = None
self.station_marketid: Optional[int] = None # Frontier MarketID
self.system_link: tk.Widget | None = None
self.system_name: tk.Tk | None = None
self.system_address: int | None = None # Frontier SystemAddress
self.system_population: int | None = None
self.station_link: tk.Widget | None = None
self.station_name: str | None = None
self.station_marketid: int | None = None # Frontier MarketID
self.on_foot = False
self._IMG_KNOWN = None
@@ -107,19 +109,19 @@ class This:
self.thread: Optional[threading.Thread] = None
self.log = None
self.log_button = None
self.log: tk.IntVar | None = None
self.log_button: ttk.Checkbutton | None = None
self.label = None
self.label: tk.Widget | None = None
self.cmdr_label = None
self.cmdr_text = None
self.cmdr_label: myNotebook.Label | None = None
self.cmdr_text: myNotebook.Label | None = None
self.user_label = None
self.user = None
self.user_label: myNotebook.Label | None = None
self.user: myNotebook.Entry | None = None
self.apikey_label = None
self.apikey = None
self.apikey_label: myNotebook.Label | None = None
self.apikey: myNotebook.Entry | None = None
this = This()
@@ -184,9 +186,9 @@ def station_url(system_name: str, station_name: str) -> str:
)
# monitor state might think these are gone, but we don't yet
if this.system and this.station:
if this.system_name and this.station_name:
return requests.utils.requote_uri(
f'https://www.edsm.net/en/system?systemName={this.system}&stationName={this.station}'
f'https://www.edsm.net/en/system?systemName={this.system_name}&stationName={this.station_name}'
)
if system_name:
@@ -248,9 +250,15 @@ def plugin_app(parent: tk.Tk) -> None:
:param parent: The tk parent to place our widgets into.
:return: See PLUGINS.md#display
"""
this.system_link = parent.children['system'] # system label in main window
# system label in main window
this.system_link = parent.nametowidget(f".{appname.lower()}.system")
if this.system_link is None:
logger.error("Couldn't look up system widget!!!")
return
this.system_link.bind_all('<<EDSMStatus>>', update_status)
this.station_link = parent.children['station'] # station label in main window
# station label in main window
this.station_link = parent.nametowidget(f".{appname.lower()}.station")
def plugin_stop() -> None:
@@ -267,7 +275,7 @@ def plugin_stop() -> None:
logger.debug('Done.')
def plugin_prefs(parent: tk.Tk, cmdr: str, is_beta: bool) -> tk.Frame:
def plugin_prefs(parent: ttk.Notebook, cmdr: str | None, is_beta: bool) -> tk.Frame:
"""
Plugin preferences setup hook.
@@ -300,7 +308,8 @@ def plugin_prefs(parent: tk.Tk, cmdr: str, is_beta: bool) -> tk.Frame:
frame, text=_('Send flight log and Cmdr status to EDSM'), variable=this.log, command=prefsvarchanged
)
this.log_button.grid(columnspan=2, padx=BUTTONX, pady=(5, 0), sticky=tk.W)
if this.log_button:
this.log_button.grid(columnspan=2, padx=BUTTONX, pady=(5, 0), sticky=tk.W)
nb.Label(frame).grid(sticky=tk.W) # big spacer
# Section heading in settings
@@ -315,61 +324,77 @@ def plugin_prefs(parent: tk.Tk, cmdr: str, is_beta: bool) -> tk.Frame:
cur_row = 10
this.label.grid(columnspan=2, padx=PADX, sticky=tk.W)
if this.label:
this.label.grid(columnspan=2, padx=PADX, sticky=tk.W)
# LANG: Game Commander name label in EDSM settings
this.cmdr_label = nb.Label(frame, text=_('Cmdr')) # Main window
this.cmdr_label.grid(row=cur_row, padx=PADX, sticky=tk.W)
this.cmdr_text = nb.Label(frame)
this.cmdr_text.grid(row=cur_row, column=1, padx=PADX, pady=PADY, sticky=tk.W)
if this.cmdr_label and this.cmdr_text:
# LANG: Game Commander name label in EDSM settings
this.cmdr_label = nb.Label(frame, text=_('Cmdr')) # Main window
this.cmdr_label.grid(row=cur_row, padx=PADX, sticky=tk.W)
this.cmdr_text = nb.Label(frame)
this.cmdr_text.grid(row=cur_row, column=1, padx=PADX, pady=PADY, sticky=tk.W)
cur_row += 1
# LANG: EDSM Commander name label in EDSM settings
this.user_label = nb.Label(frame, text=_('Commander Name')) # EDSM setting
this.user_label.grid(row=cur_row, padx=PADX, sticky=tk.W)
this.user = nb.Entry(frame)
this.user.grid(row=cur_row, column=1, padx=PADX, pady=PADY, sticky=tk.EW)
if this.user_label and this.label:
# LANG: EDSM Commander name label in EDSM settings
this.user_label = nb.Label(frame, text=_('Commander Name')) # EDSM setting
this.user_label.grid(row=cur_row, padx=PADX, sticky=tk.W)
this.user = nb.Entry(frame)
this.user.grid(row=cur_row, column=1, padx=PADX, pady=PADY, sticky=tk.EW)
cur_row += 1
# LANG: EDSM API key label
this.apikey_label = nb.Label(frame, text=_('API Key')) # EDSM setting
this.apikey_label.grid(row=cur_row, padx=PADX, sticky=tk.W)
this.apikey = nb.Entry(frame)
this.apikey.grid(row=cur_row, column=1, padx=PADX, pady=PADY, sticky=tk.EW)
if this.apikey_label and this.apikey:
# LANG: EDSM API key label
this.apikey_label = nb.Label(frame, text=_('API Key')) # EDSM setting
this.apikey_label.grid(row=cur_row, padx=PADX, sticky=tk.W)
this.apikey = nb.Entry(frame)
this.apikey.grid(row=cur_row, column=1, padx=PADX, pady=PADY, sticky=tk.EW)
prefs_cmdr_changed(cmdr, is_beta)
return frame
def prefs_cmdr_changed(cmdr: str, is_beta: bool) -> None:
def prefs_cmdr_changed(cmdr: str | None, is_beta: bool) -> None: # noqa: CCR001
"""
Handle the Commander name changing whilst Settings was open.
:param cmdr: The new current Commander name.
:param is_beta: Whether game beta was detected.
"""
this.log_button['state'] = tk.NORMAL if cmdr and not is_beta else tk.DISABLED
this.user['state'] = tk.NORMAL
this.user.delete(0, tk.END)
this.apikey['state'] = tk.NORMAL
this.apikey.delete(0, tk.END)
if this.log_button:
this.log_button['state'] = tk.NORMAL if cmdr and not is_beta else tk.DISABLED
if this.user:
this.user['state'] = tk.NORMAL
this.user.delete(0, tk.END)
if this.apikey:
this.apikey['state'] = tk.NORMAL
this.apikey.delete(0, tk.END)
if cmdr:
this.cmdr_text['text'] = f'{cmdr}{" [Beta]" if is_beta else ""}'
if this.cmdr_text:
this.cmdr_text['text'] = f'{cmdr}{" [Beta]" if is_beta else ""}'
cred = credentials(cmdr)
if cred:
this.user.insert(0, cred[0])
this.apikey.insert(0, cred[1])
if this.user:
this.user.insert(0, cred[0])
if this.apikey:
this.apikey.insert(0, cred[1])
else:
# LANG: We have no data on the current commander
this.cmdr_text['text'] = _('None')
if this.cmdr_text:
# LANG: We have no data on the current commander
this.cmdr_text['text'] = _('None')
to_set: Union[Literal['normal'], Literal['disabled']] = tk.DISABLED
if cmdr and not is_beta and this.log.get():
if cmdr and not is_beta and this.log and this.log.get():
to_set = tk.NORMAL
set_prefs_ui_states(to_set)
@@ -378,7 +403,7 @@ def prefs_cmdr_changed(cmdr: str, is_beta: bool) -> None:
def prefsvarchanged() -> None:
"""Handle the 'Send data to EDSM' tickbox changing state."""
to_set = tk.DISABLED
if this.log.get():
if this.log and this.log.get() and this.log_button:
to_set = this.log_button['state']
set_prefs_ui_states(to_set)
@@ -390,13 +415,17 @@ def set_prefs_ui_states(state: str) -> None:
:param state: the state to set each entry to
"""
this.label['state'] = state
this.cmdr_label['state'] = state
this.cmdr_text['state'] = state
this.user_label['state'] = state
this.user['state'] = state
this.apikey_label['state'] = state
this.apikey['state'] = state
if (
this.label and this.cmdr_label and this.cmdr_text and this.user_label and this.user
and this.apikey_label and this.apikey
):
this.label['state'] = state
this.cmdr_label['state'] = state
this.cmdr_text['state'] = state
this.user_label['state'] = state
this.user['state'] = state
this.apikey_label['state'] = state
this.apikey['state'] = state
def prefs_changed(cmdr: str, is_beta: bool) -> None:
@@ -406,7 +435,8 @@ def prefs_changed(cmdr: str, is_beta: bool) -> None:
:param cmdr: Name of Commander.
:param is_beta: Whether game beta was detected.
"""
config.set('edsm_out', this.log.get())
if this.log:
config.set('edsm_out', this.log.get())
if cmdr and not is_beta:
# TODO: remove this when config is rewritten.
@@ -414,17 +444,18 @@ def prefs_changed(cmdr: str, is_beta: bool) -> None:
usernames: List[str] = config.get_list('edsm_usernames', default=[])
apikeys: List[str] = config.get_list('edsm_apikeys', default=[])
if cmdr in cmdrs:
idx = cmdrs.index(cmdr)
usernames.extend([''] * (1 + idx - len(usernames)))
usernames[idx] = this.user.get().strip()
apikeys.extend([''] * (1 + idx - len(apikeys)))
apikeys[idx] = this.apikey.get().strip()
if this.user and this.apikey:
if cmdr in cmdrs:
idx = cmdrs.index(cmdr)
usernames.extend([''] * (1 + idx - len(usernames)))
usernames[idx] = this.user.get().strip()
apikeys.extend([''] * (1 + idx - len(apikeys)))
apikeys[idx] = this.apikey.get().strip()
else:
config.set('edsm_cmdrs', cmdrs + [cmdr])
usernames.append(this.user.get().strip())
apikeys.append(this.apikey.get().strip())
else:
config.set('edsm_cmdrs', cmdrs + [cmdr])
usernames.append(this.user.get().strip())
apikeys.append(this.apikey.get().strip())
config.set('edsm_usernames', usernames)
config.set('edsm_apikeys', apikeys)
@@ -494,6 +525,11 @@ def journal_entry( # noqa: C901, CCR001
this.game_version = state['GameVersion']
this.game_build = state['GameBuild']
this.system_address = state['SystemAddress']
this.system_name = state['SystemName']
this.system_population = state['SystemPopulation']
this.station_name = state['StationName']
this.station_marketid = state['MarketID']
entry = new_entry
@@ -507,50 +543,23 @@ Station: {station}
state: {state!r}
entry: {entry!r}'''
)
# Always update our system address even if we're not currently the provider for system or station, but dont update
# on events that contain "future" data, such as FSDTarget
if entry['event'] in ('Location', 'Docked', 'CarrierJump', 'FSDJump'):
this.system_address = entry.get('SystemAddress', this.system_address)
this.system = entry.get('StarSystem', this.system)
# We need pop == 0 to set the value so as to clear 'x' in systems with
# no stations.
pop = entry.get('Population')
if pop is not None:
this.system_population = pop
this.station = entry.get('StationName', this.station)
# on_foot station detection
if entry['event'] == 'Location' and entry['BodyType'] == 'Station':
this.station = entry['Body']
this.station_marketid = entry.get('MarketID', this.station_marketid)
# We might pick up StationName in DockingRequested, make sure we clear it if leaving
if entry['event'] in ('Undocked', 'FSDJump', 'SupercruiseEntry'):
this.station = None
this.station_marketid = None
if entry['event'] == 'Embark' and not entry.get('OnStation'):
# If we're embarking OnStation to a Taxi/Dropship we'll also get an
# Undocked event.
this.station = None
this.station_marketid = None
if config.get_str('station_provider') == 'EDSM':
to_set = this.station
if not this.station:
to_set = this.station_name
if not this.station_name:
if this.system_population and this.system_population > 0:
to_set = STATION_UNDOCKED
else:
to_set = ''
this.station_link['text'] = to_set
this.station_link['url'] = station_url(str(this.system), str(this.station))
this.station_link.update_idletasks()
if this.station_link:
this.station_link['text'] = to_set
this.station_link['url'] = station_url(str(this.system_name), str(this.station_name))
this.station_link.update_idletasks()
# Update display of 'EDSM Status' image
if this.system_link['text'] != system:
if this.system_link and this.system_link['text'] != system:
this.system_link['text'] = system if system else ''
this.system_link['image'] = ''
this.system_link.update_idletasks()
@@ -596,9 +605,8 @@ entry: {entry!r}'''
):
# LANG: The Inara API only accepts Live galaxy data, not Legacy galaxy data
logger.info("EDSM only accepts Live galaxy data")
# this.parent.children['status']['text'] =
this.legacy_galaxy_last_notified = datetime.now(timezone.utc)
return _("EDSM only accepts Live galaxy data")
return _("EDSM only accepts Live galaxy data") # LANG: EDSM - Only Live data
return ''
@@ -639,7 +647,7 @@ Queueing: {entry!r}'''
# Update system data
def cmdr_data(data: CAPIData, is_beta: bool) -> Optional[str]:
def cmdr_data(data: CAPIData, is_beta: bool) -> Optional[str]: # noqa: CCR001
"""
Process new CAPI data.
@@ -654,36 +662,38 @@ def cmdr_data(data: CAPIData, is_beta: bool) -> Optional[str]:
this.station_marketid = data['lastStarport']['id']
# Only trust CAPI if these aren't yet set
if not this.system:
this.system = data['lastSystem']['name']
if not this.system_name:
this.system_name = data['lastSystem']['name']
if not this.station and data['commander']['docked']:
this.station = data['lastStarport']['name']
if not this.station_name and data['commander']['docked']:
this.station_name = data['lastStarport']['name']
# TODO: Fire off the EDSM API call to trigger the callback for the icons
if config.get_str('system_provider') == 'EDSM':
this.system_link['text'] = this.system
# Do *NOT* set 'url' here, as it's set to a function that will call
# through correctly. We don't want a static string.
this.system_link.update_idletasks()
if this.system_link:
this.system_link['text'] = this.system_name
# Do *NOT* set 'url' here, as it's set to a function that will call
# through correctly. We don't want a static string.
this.system_link.update_idletasks()
if config.get_str('station_provider') == 'EDSM':
if data['commander']['docked'] or this.on_foot and this.station:
this.station_link['text'] = this.station
if this.station_link:
if data['commander']['docked'] or this.on_foot and this.station_name:
this.station_link['text'] = this.station_name
elif data['lastStarport']['name'] and data['lastStarport']['name'] != "":
this.station_link['text'] = STATION_UNDOCKED
elif data['lastStarport']['name'] and data['lastStarport']['name'] != "":
this.station_link['text'] = STATION_UNDOCKED
else:
this.station_link['text'] = ''
else:
this.station_link['text'] = ''
# Do *NOT* set 'url' here, as it's set to a function that will call
# through correctly. We don't want a static string.
# Do *NOT* set 'url' here, as it's set to a function that will call
# through correctly. We don't want a static string.
this.station_link.update_idletasks()
this.station_link.update_idletasks()
if not this.system_link['text']:
if this.system_link and not this.system_link['text']:
this.system_link['text'] = system
this.system_link['image'] = ''
this.system_link.update_idletasks()
@@ -759,9 +769,12 @@ def worker() -> None: # noqa: CCR001 C901 # Cant be broken up currently
retrying = 0
while retrying < 3:
if item is None:
item = cast(Tuple[str, str, str, Mapping[str, Any]], ("", {}))
should_skip, new_item = killswitch.check_killswitch(
'plugins.edsm.worker',
item if item is not None else cast(Tuple[str, Mapping[str, Any]], ("", {})),
item,
logger
)
@@ -838,8 +851,12 @@ def worker() -> None: # noqa: CCR001 C901 # Cant be broken up currently
if any(p for p in pending if p['event'] in ('CarrierJump', 'FSDJump', 'Location', 'Docked')):
data_elided = data.copy()
data_elided['apiKey'] = '<elided>'
data_elided['message'] = data_elided['message'].decode('utf-8')
data_elided['commanderName'] = data_elided['commanderName'].decode('utf-8')
if isinstance(data_elided['message'], bytes):
data_elided['message'] = data_elided['message'].decode('utf-8')
if isinstance(data_elided['commanderName'], bytes):
data_elided['commanderName'] = data_elided['commanderName'].decode('utf-8')
logger.trace_if(
'journal.locations',
"pending has at least one of ('CarrierJump', 'FSDJump', 'Location', 'Docked')"
@@ -858,11 +875,11 @@ def worker() -> None: # noqa: CCR001 C901 # Cant be broken up currently
'journal.locations', f'Overall POST data (elided) is:\n{json.dumps(data_elided, indent=2)}'
)
r = this.session.post(TARGET_URL, data=data, timeout=_TIMEOUT)
logger.trace_if('plugin.edsm.api', f'API response content: {r.content!r}')
r.raise_for_status()
response = this.session.post(TARGET_URL, data=data, timeout=_TIMEOUT)
logger.trace_if('plugin.edsm.api', f'API response content: {response.content!r}')
response.raise_for_status()
reply = r.json()
reply = response.json()
msg_num = reply['msgnum']
msg = reply['msg']
# 1xx = OK
@@ -893,7 +910,7 @@ def worker() -> None: # noqa: CCR001 C901 # Cant be broken up currently
# Update main window's system status
this.lastlookup = r
# calls update_status in main thread
if not config.shutting_down:
if not config.shutting_down and this.system_link is not None:
this.system_link.event_generate('<<EDSMStatus>>', when="tail")
if r['msgnum'] // 100 != 1: # type: ignore
@@ -996,18 +1013,19 @@ def update_status(event=None) -> None:
# msgnum: 1xx = OK, 2xx = fatal error, 3xx = error, 4xx = ignorable errors.
def edsm_notify_system(reply: Mapping[str, Any]) -> None:
"""Update the image next to the system link."""
if not reply:
this.system_link['image'] = this._IMG_ERROR
# LANG: EDSM Plugin - Error connecting to EDSM API
plug.show_error(_("Error: Can't connect to EDSM"))
if this.system_link is not None:
if not reply:
this.system_link['image'] = this._IMG_ERROR
# LANG: EDSM Plugin - Error connecting to EDSM API
plug.show_error(_("Error: Can't connect to EDSM"))
elif reply['msgnum'] // 100 not in (1, 4):
this.system_link['image'] = this._IMG_ERROR
# LANG: EDSM Plugin - Error message from EDSM API
plug.show_error(_('Error: EDSM {MSG}').format(MSG=reply['msg']))
elif reply['msgnum'] // 100 not in (1, 4):
this.system_link['image'] = this._IMG_ERROR
# LANG: EDSM Plugin - Error message from EDSM API
plug.show_error(_('Error: EDSM {MSG}').format(MSG=reply['msg']))
elif reply.get('systemCreated'):
this.system_link['image'] = this._IMG_NEW
elif reply.get('systemCreated'):
this.system_link['image'] = this._IMG_NEW
else:
this.system_link['image'] = this._IMG_KNOWN
else:
this.system_link['image'] = this._IMG_KNOWN
+51 -67
View File
@@ -15,10 +15,9 @@
# Thus you **MUST** check if any imports you add in this file are only
# referenced in this file (or only in any other core plugin), and if so...
#
# YOU MUST ENSURE THAT PERTINENT ADJUSTMENTS ARE MADE IN `setup.py`
# SO AS TO ENSURE THE FILES ARE ACTUALLY PRESENT IN AN END-USER
# INSTALLATION ON WINDOWS.
#
# YOU MUST ENSURE THAT PERTINENT ADJUSTMENTS ARE MADE IN
# `Build-exe-and-msi.py` SO AS TO ENSURE THE FILES ARE ACTUALLY PRESENT
# IN AN END-USER INSTALLATION ON WINDOWS.
#
# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $#
# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $# ! $#
@@ -31,6 +30,7 @@ from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
from operator import itemgetter
from threading import Lock, Thread
from tkinter import ttk
from typing import TYPE_CHECKING, Any, Callable, Deque, Dict, List, Mapping, NamedTuple, Optional
from typing import OrderedDict as OrderedDictT
from typing import Sequence, Union, cast
@@ -43,7 +43,7 @@ import myNotebook as nb # noqa: N813
import plug
import timeout_session
from companion import CAPIData
from config import applongname, appversion, config, debug_senders
from config import applongname, appname, appversion, config, debug_senders
from EDMCLogging import get_main_logger
from monitor import monitor
from ttkHyperlinkLabel import HyperlinkLabel
@@ -119,7 +119,7 @@ class This:
# Main window clicks
self.system_link: tk.Widget = None # type: ignore
self.system: Optional[str] = None # type: ignore
self.system_name: Optional[str] = None # type: ignore
self.system_address: Optional[str] = None # type: ignore
self.system_population: Optional[int] = None
self.station_link: tk.Widget = None # type: ignore
@@ -131,7 +131,7 @@ class This:
self.log_button: nb.Checkbutton
self.label: HyperlinkLabel
self.apikey: nb.Entry
self.apikey_label: HyperlinkLabel
self.apikey_label: tk.Label
self.events: Dict[Credentials, Deque[Event]] = defaultdict(deque)
self.event_lock: Lock = threading.Lock() # protects events, for use when rewriting events
@@ -169,10 +169,12 @@ if DEBUG:
def system_url(system_name: str) -> str:
"""Get a URL for the current system."""
if this.system_address:
return requests.utils.requote_uri(f'https://inara.cz/galaxy-starsystem/?search={this.system_address}')
return requests.utils.requote_uri(f'https://inara.cz/galaxy-starsystem/'
f'?search={this.system_address}')
elif system_name:
return requests.utils.requote_uri(f'https://inara.cz/galaxy-starsystem/?search={system_name}')
return requests.utils.requote_uri(f'https://inara.cz/galaxy-starsystem/'
f'?search={system_name}')
return ''
@@ -188,11 +190,13 @@ def station_url(system_name: str, station_name: str) -> str:
:return: A URL to inara for the given system and station
"""
if system_name and station_name:
return requests.utils.requote_uri(f'https://inara.cz/galaxy-station/?search={system_name}%20[{station_name}]')
return requests.utils.requote_uri(f'https://inara.cz/galaxy-station/'
f'?search={system_name}%20[{station_name}]')
# monitor state might think these are gone, but we don't yet
if this.system and this.station:
return requests.utils.requote_uri(f'https://inara.cz/galaxy-station/?search={this.system}%20[{this.station}]')
if this.system_name and this.station:
return requests.utils.requote_uri(f'https://inara.cz/galaxy-station/'
f'?search={this.system_name}%20[{this.station}]')
if system_name:
return system_url(system_name)
@@ -218,8 +222,10 @@ def plugin_start3(plugin_dir: str) -> str:
def plugin_app(parent: tk.Tk) -> None:
"""Plugin UI setup Hook."""
this.parent = parent
this.system_link = parent.children['system'] # system label in main window
this.station_link = parent.children['station'] # station label in main window
# system label in main window
this.system_link = parent.nametowidget(f".{appname.lower()}.system")
# station label in main window
this.station_link = parent.nametowidget(f".{appname.lower()}.station")
this.system_link.bind_all('<<InaraLocation>>', update_location)
this.system_link.bind_all('<<InaraShip>>', update_ship)
@@ -236,7 +242,7 @@ def plugin_stop() -> None:
logger.debug('Done.')
def plugin_prefs(parent: tk.Tk, cmdr: str, is_beta: bool) -> tk.Frame:
def plugin_prefs(parent: ttk.Notebook, cmdr: str, is_beta: bool) -> tk.Frame:
"""Plugin Preferences UI hook."""
x_padding = 10
x_button_padding = 12 # indent Checkbuttons and Radiobuttons
@@ -369,6 +375,31 @@ def journal_entry( # noqa: C901, CCR001
:return: str - empty if no error, else error string.
"""
# This overall killswitch check is first in case, e.g. an EDMC bug is
# causing users to spam Inara with 'URL provider' queries, and we want to
# stop that.
should_return: bool
new_entry: Dict[str, Any] = {}
should_return, new_entry = killswitch.check_killswitch('plugins.inara.journal', entry, logger)
if should_return:
plug.show_error(_('Inara disabled. See Log.')) # LANG: INARA support disabled via killswitch
logger.trace('returning due to killswitch match')
return ''
# But then we update all the tracking copies before any other checks,
# because they're relevant for URL providing even if *sending* isn't
# appropriate.
this.on_foot = state['OnFoot']
event_name: str = entry['event']
this.cmdr = cmdr
this.FID = state['FID']
this.multicrew = bool(state['Role'])
this.system_name = state['SystemName']
this.system_address = state['SystemAddress']
this.station = state['StationName']
this.station_marketid = state['MarketID']
if not monitor.is_live_galaxy():
# Since Update 14 on 2022-11-29 Inara only accepts Live data.
if (
@@ -380,18 +411,11 @@ def journal_entry( # noqa: C901, CCR001
):
# LANG: The Inara API only accepts Live galaxy data, not Legacy galaxy data
logger.info(_("Inara only accepts Live galaxy data"))
# this.parent.children['status']['text'] =
this.legacy_galaxy_last_notified = datetime.now(timezone.utc)
return _("Inara only accepts Live galaxy data")
return _("Inara only accepts Live galaxy data") # LANG: Inara - Only Live data
return ''
should_return, new_entry = killswitch.check_killswitch('plugins.inara.journal', entry, logger)
if should_return:
plug.show_error(_('Inara disabled. See Log.')) # LANG: INARA support disabled via killswitch
logger.trace('returning due to killswitch match')
return ''
should_return, new_entry = killswitch.check_killswitch(
f'plugins.inara.journal.event.{entry["event"]}', new_entry, logger
)
@@ -401,12 +425,6 @@ def journal_entry( # noqa: C901, CCR001
return ''
entry = new_entry
this.on_foot = state['OnFoot']
event_name: str = entry['event']
this.cmdr = cmdr
this.FID = state['FID']
this.multicrew = bool(state['Role'])
if event_name == 'LoadGame' or this.newuser:
# clear cached state
if event_name == 'LoadGame':
@@ -427,10 +445,6 @@ def journal_entry( # noqa: C901, CCR001
this.loadout = None
this.fleet = None
this.shipswap = False
this.system = None
this.system_address = None
this.station = None
this.station_marketid = None
elif event_name in ('Resurrect', 'ShipyardBuy', 'ShipyardSell', 'SellShipOnRebuy'):
# Events that mean a significant change in credits, so we should send credits after next "Update"
@@ -439,35 +453,6 @@ def journal_entry( # noqa: C901, CCR001
elif event_name in ('ShipyardNew', 'ShipyardSwap') or (event_name == 'Location' and entry['Docked']):
this.suppress_docked = True
# Always update our system address even if we're not currently the provider for system or station, but dont update
# on events that contain "future" data, such as FSDTarget
if entry['event'] in ('Location', 'Docked', 'CarrierJump', 'FSDJump'):
this.system_address = entry.get('SystemAddress') or this.system_address
this.system = entry.get('StarSystem') or this.system
# We need pop == 0 to set the value so as to clear 'x' in systems with
# no stations.
pop: Optional[int] = entry.get('Population')
if pop is not None:
this.system_population = pop
this.station = entry.get('StationName', this.station)
# on_foot station detection
if entry['event'] == 'Location' and entry['BodyType'] == 'Station':
this.station = entry['Body']
this.station_marketid = entry.get('MarketID', this.station_marketid) or this.station_marketid
# We might pick up StationName in DockingRequested, make sure we clear it if leaving
if event_name in ('Undocked', 'FSDJump', 'SupercruiseEntry'):
this.station = None
this.station_marketid = None
if entry['event'] == 'Embark' and not entry.get('OnStation'):
# If we're embarking OnStation to a Taxi/Dropship we'll also get an
# Undocked event.
this.station = None
this.station_marketid = None
if config.get_int('inara_out') and not is_beta and not this.multicrew and credentials(cmdr):
current_credentials = Credentials(this.cmdr, this.FID, str(credentials(this.cmdr)))
try:
@@ -1154,7 +1139,7 @@ def journal_entry( # noqa: C901, CCR001
to_send_data: Optional[Dict[str, Any]] = {} # This is a glorified sentinel until lower down.
# On Horizons, neither of these exist on TouchDown
star_system_name = entry.get('StarSystem', this.system)
star_system_name = entry.get('StarSystem', this.system_name)
body_name = entry.get('Body', state['Body'] if state['BodyType'] == 'Planet' else None)
if star_system_name is None:
@@ -1373,7 +1358,7 @@ def journal_entry( # noqa: C901, CCR001
# Only actually change URLs if we are current provider.
if config.get_str('system_provider') == 'Inara':
this.system_link['text'] = this.system
this.system_link['text'] = this.system_name
# Do *NOT* set 'url' here, as it's set to a function that will call
# through correctly. We don't want a static string.
this.system_link.update_idletasks()
@@ -1403,14 +1388,14 @@ def cmdr_data(data: CAPIData, is_beta): # noqa: CCR001
this.station_marketid = data['commander']['docked'] and data['lastStarport']['id']
# Only trust CAPI if these aren't yet set
this.system = this.system if this.system else data['lastSystem']['name']
this.system_name = this.system_name if this.system_name else data['lastSystem']['name']
if not this.station and data['commander']['docked']:
this.station = data['lastStarport']['name']
# Override standard URL functions
if config.get_str('system_provider') == 'Inara':
this.system_link['text'] = this.system
this.system_link['text'] = this.system_name
# Do *NOT* set 'url' here, as it's set to a function that will call
# through correctly. We don't want a static string.
this.system_link.update_idletasks()
@@ -1537,7 +1522,6 @@ def new_add_event(
def clean_event_list(event_list: List[Event]) -> List[Event]:
"""Check for killswitched events and remove or modify them as requested."""
out = []
for e in event_list:
bad, new_event = killswitch.check_killswitch(f'plugins.inara.worker.{e.name}', e.data, logger)
if bad:
+39 -61
View File
@@ -19,7 +19,6 @@ from EDMCLogging import edmclogger, get_main_logger
from hotkey import hotkeymgr
from l10n import Translations
from monitor import monitor
from myNotebook import Notebook
from theme import theme
from ttkHyperlinkLabel import HyperlinkLabel
@@ -182,7 +181,7 @@ if sys.platform == 'darwin':
elif sys.platform == 'win32':
import ctypes
import winreg
from ctypes.wintypes import HINSTANCE, HWND, LPARAM, LPCWSTR, LPVOID, LPWSTR, MAX_PATH, POINT, RECT, SIZE, UINT
from ctypes.wintypes import HINSTANCE, HWND, LPCWSTR, LPWSTR, MAX_PATH, POINT, RECT, SIZE, UINT
is_wine = False
try:
WINE_REGISTRY_KEY = r'HKEY_LOCAL_MACHINE\Software\Wine'
@@ -190,22 +189,9 @@ elif sys.platform == 'win32':
winreg.OpenKey(reg, WINE_REGISTRY_KEY)
is_wine = True
except OSError:
except OSError: # Assumed to be 'path not found', i.e. not-wine
pass
# https://msdn.microsoft.com/en-us/library/windows/desktop/bb762115
BIF_RETURNONLYFSDIRS = 0x00000001
BIF_USENEWUI = 0x00000050
BFFM_INITIALIZED = 1
BFFM_SETSELECTION = 0x00000467
BrowseCallbackProc = ctypes.WINFUNCTYPE(ctypes.c_int, HWND, ctypes.c_uint, LPARAM, LPARAM)
class BROWSEINFO(ctypes.Structure):
"""Windows file browser fields."""
_fields_ = [("hwndOwner", HWND), ("pidlRoot", LPVOID), ("pszDisplayName", LPWSTR), ("lpszTitle", LPCWSTR),
("ulFlags", UINT), ("lpfn", BrowseCallbackProc), ("lParam", LPCWSTR), ("iImage", ctypes.c_int)]
CalculatePopupWindowPosition = None
if not is_wine:
try:
@@ -279,7 +265,7 @@ class PreferencesDialog(tk.Toplevel):
frame = ttk.Frame(self)
frame.grid(sticky=tk.NSEW)
notebook = nb.Notebook(frame)
notebook: ttk.Notebook = nb.Notebook(frame)
notebook.bind('<<NotebookTabChanged>>', self.tabchanged) # Recompute on tab change
self.PADX = 10
@@ -333,7 +319,7 @@ class PreferencesDialog(tk.Toplevel):
):
self.geometry(f"+{position.left}+{position.top}")
def __setup_output_tab(self, root_notebook: nb.Notebook) -> None:
def __setup_output_tab(self, root_notebook: ttk.Notebook) -> None:
output_frame = nb.Frame(root_notebook)
output_frame.columnconfigure(0, weight=1)
@@ -418,13 +404,13 @@ class PreferencesDialog(tk.Toplevel):
# LANG: Label for 'Output' Settings/Preferences tab
root_notebook.add(output_frame, text=_('Output')) # Tab heading in settings
def __setup_plugin_tabs(self, notebook: Notebook) -> None:
def __setup_plugin_tabs(self, notebook: ttk.Notebook) -> None:
for plugin in plug.PLUGINS:
plugin_frame = plugin.get_prefs(notebook, monitor.cmdr, monitor.is_beta)
if plugin_frame:
notebook.add(plugin_frame, text=plugin.name)
def __setup_config_tab(self, notebook: Notebook) -> None: # noqa: CCR001
def __setup_config_tab(self, notebook: ttk.Notebook) -> None: # noqa: CCR001
config_frame = nb.Frame(notebook)
config_frame.columnconfigure(1, weight=1)
row = AutoInc(start=1)
@@ -471,6 +457,25 @@ class PreferencesDialog(tk.Toplevel):
state=tk.NORMAL if config.get_str('journaldir') else tk.DISABLED
).grid(column=2, pady=self.PADY, sticky=tk.EW, row=row.get())
# CAPI settings
self.capi_fleetcarrier = tk.BooleanVar(value=config.get_bool('capi_fleetcarrier'))
ttk.Separator(config_frame, orient=tk.HORIZONTAL).grid(
columnspan=4, padx=self.PADX, pady=self.PADY*4, sticky=tk.EW, row=row.get()
)
nb.Label(
config_frame,
text=_('CAPI Settings') # LANG: Settings > Configuration - Label for CAPI section
).grid(padx=self.PADX, sticky=tk.W, row=row.get())
nb.Checkbutton(
config_frame,
# LANG: Configuration - Enable or disable the Fleet Carrier CAPI calls
text=_('Enable Fleetcarrier CAPI Queries'),
variable=self.capi_fleetcarrier
).grid(columnspan=4, padx=self.PADX, pady=(5, 0), sticky=tk.W, row=row.get())
if sys.platform in ('darwin', 'win32'):
ttk.Separator(config_frame, orient=tk.HORIZONTAL).grid(
columnspan=4, padx=self.PADX, pady=self.PADY*4, sticky=tk.EW, row=row.get()
@@ -675,7 +680,7 @@ class PreferencesDialog(tk.Toplevel):
# LANG: Label for 'Configuration' tab in Settings
notebook.add(config_frame, text=_('Configuration'))
def __setup_privacy_tab(self, notebook: Notebook) -> None:
def __setup_privacy_tab(self, notebook: ttk.Notebook) -> None:
frame = nb.Frame(notebook)
self.hide_multicrew_captain = tk.BooleanVar(value=config.get_bool('hide_multicrew_captain', default=False))
self.hide_private_group = tk.BooleanVar(value=config.get_bool('hide_private_group', default=False))
@@ -697,7 +702,7 @@ class PreferencesDialog(tk.Toplevel):
notebook.add(frame, text=_('Privacy')) # LANG: Preferences privacy tab title
def __setup_appearance_tab(self, notebook: Notebook) -> None:
def __setup_appearance_tab(self, notebook: ttk.Notebook) -> None:
self.languages = Translations.available_names()
# Appearance theme and language setting
# LANG: The system default language choice in Settings > Appearance
@@ -887,7 +892,7 @@ class PreferencesDialog(tk.Toplevel):
# LANG: Label for Settings > Appearance tab
notebook.add(appearance_frame, text=_('Appearance')) # Tab heading in settings
def __setup_plugin_tab(self, notebook: Notebook) -> None: # noqa: CCR001
def __setup_plugin_tab(self, notebook: ttk.Notebook) -> None: # noqa: CCR001
# Plugin settings and info
plugins_frame = nb.Frame(notebook)
plugins_frame.columnconfigure(0, weight=1)
@@ -1026,42 +1031,13 @@ class PreferencesDialog(tk.Toplevel):
:param title: Title of the window
:param pathvar: the path to start the dialog on
"""
import locale
# If encoding isn't UTF-8 we can't use the tkinter dialog
current_locale = locale.getlocale(locale.LC_CTYPE)
directory = None
if sys.platform == 'win32' and current_locale[1] not in ('utf8', 'UTF8', 'utf-8', 'UTF-8'):
def browsecallback(hwnd, uMsg, lParam, lpData): # noqa: N803 # Windows API convention
# set initial folder
if uMsg == BFFM_INITIALIZED and lpData:
ctypes.windll.user32.SendMessageW(hwnd, BFFM_SETSELECTION, 1, lpData)
return 0
browseInfo = BROWSEINFO() # noqa: N806 # Windows convention
browseInfo.lpszTitle = title
browseInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI
browseInfo.lpfn = BrowseCallbackProc(browsecallback)
browseInfo.lParam = pathvar.get().startswith('~') and join(config.home_path,
pathvar.get()[2:]) or pathvar.get()
ctypes.windll.ole32.CoInitialize(None)
pidl = ctypes.windll.shell32.SHBrowseForFolderW(ctypes.byref(browseInfo))
if pidl:
path = ctypes.create_unicode_buffer(MAX_PATH)
ctypes.windll.shell32.SHGetPathFromIDListW(pidl, path)
ctypes.windll.ole32.CoTaskMemFree(pidl)
directory = path.value
else:
directory = None
else:
import tkinter.filedialog
directory = tkinter.filedialog.askdirectory(
parent=self,
initialdir=expanduser(pathvar.get()),
title=title,
mustexist=tk.TRUE
)
import tkinter.filedialog
directory = tkinter.filedialog.askdirectory(
parent=self,
initialdir=expanduser(pathvar.get()),
title=title,
mustexist=tk.TRUE
)
if directory:
pathvar.set(directory)
@@ -1188,8 +1164,8 @@ class PreferencesDialog(tk.Toplevel):
:return: "break" as a literal, to halt processing
"""
good = hotkeymgr.fromevent(event)
if good:
(hotkey_code, hotkey_mods) = good
if good and isinstance(good, tuple):
hotkey_code, hotkey_mods = good
event.widget.delete(0, tk.END)
event.widget.insert(0, hotkeymgr.display(hotkey_code, hotkey_mods))
if hotkey_code:
@@ -1245,6 +1221,8 @@ class PreferencesDialog(tk.Toplevel):
else:
config.set('journaldir', logdir)
config.set('capi_fleetcarrier', self.capi_fleetcarrier.get())
if sys.platform in ('darwin', 'win32'):
config.set('hotkey_code', self.hotkey_code)
config.set('hotkey_mods', self.hotkey_mods)
+36 -7
View File
@@ -106,12 +106,12 @@ if sys.platform == 'darwin' and getattr(sys, 'frozen', False): # noqa: C901 # i
def handleEvent_withReplyEvent_(self, event, replyEvent) -> None: # noqa: N802 N803 # Required to override
"""Actual event handling from NSAppleEventManager."""
protocolhandler.lasturl = urllib.parse.unquote( # noqa: F821: type: ignore # Its going to be a DPH in
protocolhandler.lasturl = urllib.parse.unquote( # noqa: F821: type: ignore # It's going to be a DPH in
# this code
event.paramDescriptorForKeyword_(keyDirectObject).stringValue()
).strip()
protocolhandler.master.after(DarwinProtocolHandler.POLL, protocolhandler.poll) # noqa: F821: type: ignore
protocolhandler.master.after(DarwinProtocolHandler.POLL, protocolhandler.poll) # noqa: F821 # type: ignore
elif (config.auth_force_edmc_protocol
@@ -121,12 +121,16 @@ elif (config.auth_force_edmc_protocol
and not is_wine
and not config.auth_force_localserver
)):
# This could be false if you use auth_force_edmc_protocol, but then you get to keep the pieces
assert sys.platform == 'win32'
# spell-checker: words HBRUSH HICON WPARAM wstring WNDCLASS HMENU HGLOBAL
from ctypes import windll # type: ignore
from ctypes import POINTER, WINFUNCTYPE, Structure, byref, c_long, c_void_p, create_unicode_buffer, wstring_at
from ctypes import ( # type: ignore
POINTER, WINFUNCTYPE, Structure, byref, c_long, c_void_p, create_unicode_buffer, wstring_at
)
from ctypes.wintypes import (
ATOM, BOOL, DWORD, HBRUSH, HGLOBAL, HICON, HINSTANCE, HMENU, HWND, INT, LPARAM, LPCWSTR, LPVOID, LPWSTR, MSG,
UINT, WPARAM
ATOM, BOOL, DWORD, HBRUSH, HGLOBAL, HICON, HINSTANCE, HMENU, HWND, INT, LPARAM, LPCWSTR, LPMSG, LPVOID, LPWSTR,
MSG, UINT, WPARAM
)
class WNDCLASS(Structure):
@@ -157,11 +161,25 @@ elif (config.auth_force_edmc_protocol
CreateWindowExW.restype = HWND
RegisterClassW = windll.user32.RegisterClassW
RegisterClassW.argtypes = [POINTER(WNDCLASS)]
DefWindowProcW = windll.user32.DefWindowProcW
# DefWindowProcW
# Ref: <https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-defwindowprocw>
# LRESULT DefWindowProcW([in] HWND hWnd,[in] UINT Msg,[in] WPARAM wParam,[in] LPARAM lParam);
# As per example at <https://docs.python.org/3/library/ctypes.html#ctypes.WINFUNCTYPE>
prototype = WINFUNCTYPE(c_long, HWND, UINT, WPARAM, LPARAM)
paramflags = (1, "hWnd"), (1, "Msg"), (1, "wParam"), (1, "lParam")
DefWindowProcW = prototype(("DefWindowProcW", windll.user32), paramflags)
GetParent = windll.user32.GetParent
SetForegroundWindow = windll.user32.SetForegroundWindow
GetMessageW = windll.user32.GetMessageW
# <https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessagew>
# NB: Despite 'BOOL' return type, it *can* be >0, 0 or -1, so is actually
# c_long
prototype = WINFUNCTYPE(c_long, LPMSG, HWND, UINT, UINT)
paramflags = (1, "lpMsg"), (1, "hWnd"), (1, "wMsgFilterMin"), (1, "wMsgFilterMax")
GetMessageW = prototype(("GetMessageW", windll.user32), paramflags)
TranslateMessage = windll.user32.TranslateMessage
DispatchMessageW = windll.user32.DispatchMessageW
PostThreadMessageW = windll.user32.PostThreadMessageW
@@ -303,6 +321,17 @@ elif (config.auth_force_edmc_protocol
msg = MSG()
# Calls GetMessageW: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessagew
# Something is off with the return from this, meaning you'll see:
# Exception ignored on converting result of ctypes callback function: <function WndProc
# at 0x000001F5B8738FE0>
# Traceback (most recent call last):
# File "C:\Users\Athan\Documents\Devel\EDMarketConnector\protocol.py", line 323, in worker
# while int(GetMessageW(byref(msg), None, 0, 0)) != 0:
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# TypeError: 'c_long' object cannot be interpreted as an integer
#
# But it does actually work. Either getting a non-0 value and
# entering the loop, or getting 0 and exiting it.
while GetMessageW(byref(msg), None, 0, 0) != 0:
logger.trace_if('frontier-auth.windows', f'DDE message of type: {msg.message}')
if msg.message == WM_DDE_EXECUTE:
+1 -1
View File
@@ -11,7 +11,7 @@ addopts = "--cov . --cov plugins --cov-report=term-missing --no-cov-on-fail"
# --cov-fail-under 80"
[tool.coverage.run]
omit = [ "tests/*", "venv/*", "dist.win32/*" ]
omit = [ "tests/*", "venv/*", ".venv/*", "dist.win32/*" ]
plugins = [ "coverage_conditional_plugin" ]
[tool.coverage.coverage_conditional_plugin.rules]
+13 -9
View File
@@ -5,7 +5,7 @@ wheel
# We can't rely on just picking this up from either the base (not venv),
# or venv-init-time version. Specify here so that dependabot will prod us
# about new versions.
setuptools==65.6.3
setuptools==66.0.0
# Static analysis tools
flake8==6.0.0
@@ -13,20 +13,24 @@ flake8-annotations-coverage==0.0.6
flake8-cognitive-complexity==0.1.0
flake8-comprehensions==3.10.1
flake8-docstrings==1.6.0
isort==5.11.1
flake8-isort==5.0.3
isort==5.11.4
flake8-isort==6.0.0
flake8-json==21.7.0
flake8-noqa==1.3.0
flake8-polyfill==1.0.2
flake8-use-fstring==1.4
mypy==0.991
pep8-naming==0.13.2
pep8-naming==0.13.3
safety==2.3.5
types-requests==2.28.11.5
types-requests==2.28.11.7
types-pkg-resources==0.1.3
# Code formatting tools
autopep8==2.0.0
autopep8==2.0.1
# Git pre-commit checking
pre-commit==2.21.0
# HTML changelogs
grip==4.6.1
@@ -40,10 +44,10 @@ lxml==4.9.2
py2exe==0.13.0.0; sys_platform == 'win32'
# Testing
pytest==7.2.0
pytest==7.2.1
pytest-cov==4.0.0 # Pytest code coverage support
coverage[toml]==6.5.0 # pytest-cov dep. This is here to ensure that it includes TOML support for pyproject.toml configs
coverage-conditional-plugin==0.7.0
coverage[toml]==7.0.5 # pytest-cov dep. This is here to ensure that it includes TOML support for pyproject.toml configs
coverage-conditional-plugin==0.8.0
# For manipulating folder permissions and the like.
pywin32==305; sys_platform == 'win32'
+5 -2
View File
@@ -1,6 +1,9 @@
certifi==2022.12.7
requests==2.28.1
watchdog==2.2.0
requests==2.28.2
# requests depends on this now ?
charset-normalizer==2.1.1
watchdog==2.2.1
# Commented out because this doesn't package well with py2exe
infi.systray==0.1.12; sys_platform == 'win32'
# argh==0.26.2 watchdog dep
+8
View File
@@ -0,0 +1,8 @@
#!/bin/sh
set -e
# NB: EDMC_PATH is replaced with the correct value by the
# scripts/linux-setup.sh script. So, no, there's no missing '$' here.
cd EDMC_PATH
python3 EDMarketConnector.py
+2 -2
View File
@@ -139,7 +139,7 @@ def scan_file(path: pathlib.Path) -> list[ast.Call]:
return out
def scan_directory(path: pathlib.Path, skip: list[pathlib.Path] = None) -> dict[pathlib.Path, list[ast.Call]]:
def scan_directory(path: pathlib.Path, skip: list[pathlib.Path] | None = None) -> dict[pathlib.Path, list[ast.Call]]:
"""
Scan a directory for expected callsites.
@@ -293,7 +293,7 @@ def generate_lang_template(data: dict[pathlib.Path, list[ast.Call]]) -> str:
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--directory', help='Directory to search from', default='.')
parser.add_argument('--ignore', action='append', help='directories to ignore', default=['venv', '.git'])
parser.add_argument('--ignore', action='append', help='directories to ignore', default=['venv', '.venv', '.git'])
group = parser.add_mutually_exclusive_group()
group.add_argument('--json', action='store_true', help='JSON output')
group.add_argument('--lang', help='en.template "strings" output to specified file, "-" for stdout')
+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/env bash
#
# Set up necessary support files to make running EDMarketConnect streamlined
# on Linux.
#
###########################################################################
# Shell script launcher
#
# This needs to be in an appropriate component of $PATH so that the
# reference in the .desktop file will work.
###########################################################################
#######################################################
# Determine where edmarketconnector.sh needs to go
#######################################################
# Really we need this to be "${HOME}/bin", so check that is in $PATH
if [[ ":$PATH:" != *":$HOME/bin:"* ]];
then
echo "You need to have '${HOME}/bin' in your PATH"
echo "Please fix this (might require relogging) and try again"
exit 1
fi
EDMC_BIN_PATH="${HOME}/bin"
if [ ! -d "${EDMC_BIN_PATH}" ];
then
echo "'${EDMC_BIN_PATH}' must exist and be a directory!"
exit 2
fi
#######################################################
#######################################################
# Determine where the source is located
#######################################################
# We know where this script is situated within an unzip/git clone of
# the source code, so set EDMC_PATH based on that.
# This is in `scripts/` of the source, so one directory up
EDMC_PATH="$(dirname $0)/.."
# And we need the *full* absolute path
EDMC_PATH="$(realpath ${EDMC_PATH})"
echo "EDMC_PATH = ${EDMC_PATH}"
#######################################################
#######################################################
# Copy an edited version of edmarketconnector.sh into place
#######################################################
echo "Copying launcher shell script into place..."
sed -e "s#EDMC_PATH#${EDMC_PATH}#g;" \
< "${EDMC_PATH}/scripts/edmarketconnector.sh" \
> "${EDMC_BIN_PATH}/edmarketconnector"
#######################################################
###########################################################################
###########################################################################
# Desktop file
#
# This needs to be in a path where any XDG-compliant environment will be
# able to find it.
###########################################################################
echo "Copying .desktop file into place ..."
install -d -m700 "${HOME}/.local/share/applications"
install -t "${HOME}/.local/share/applications" "${EDMC_PATH}/io.edcd.EDMarketConnector.desktop"
###########################################################################
###########################################################################
# Icon file
#
# This needs to be in a path where any XDG-compliant environment will be
# able to find it.
###########################################################################
echo "Copying icon file into place..."
install -d -m700 "${HOME}/.local/share/icons/hicolor/512x512/apps"
install -t "${HOME}/.local/share/icons/hicolor/512x512/apps" "${EDMC_PATH}/io.edcd.EDMarketConnector.png"
###########################################################################
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
#
# Run mypy checks against all the relevant files
# We assume that all `.py` files in git should be checked, and *only* those.
mypy $@ $(git ls-tree --full-tree -r --name-only HEAD | grep -E '\.py$')
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Find the reverse dependencies of a package according to pip."""
import sys
import pkg_resources
def find_reverse_deps(package_name: str):
"""
Find the packages that depend on the named one.
:param package_name: Target package.
:return: List of packages that depend on this one.
"""
return [
pkg.project_name for pkg in pkg_resources.WorkingSet()
if package_name in {req.project_name for req in pkg.requires()}
]
if __name__ == '__main__':
print(find_reverse_deps(sys.argv[1]))
+13 -13
View File
@@ -5,7 +5,7 @@ import sys
import tkinter
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING, Any, AnyStr, Callable, Dict, List, NamedTuple, Optional, Sequence, cast
from typing import TYPE_CHECKING, Any, AnyStr, Callable, NamedTuple, Sequence, cast
import companion
import EDMCLogging
@@ -45,7 +45,7 @@ RANK_LINES_END = 9
POWERPLAY_LINES_START = 9
def status(data: Dict[str, Any]) -> List[List[str]]:
def status(data: dict[str, Any]) -> list[list[str]]:
"""
Get the current status of the cmdr referred to by data.
@@ -211,7 +211,7 @@ def status(data: Dict[str, Any]) -> List[List[str]]:
return res
def export_status(data: Dict[str, Any], filename: AnyStr) -> None:
def export_status(data: dict[str, Any], filename: AnyStr) -> None:
"""
Export status data to a CSV file.
@@ -236,21 +236,21 @@ class ShipRet(NamedTuple):
value: str
def ships(companion_data: Dict[str, Any]) -> List[ShipRet]:
def ships(companion_data: dict[str, Any]) -> list[ShipRet]:
"""
Return a list of 5 tuples of ship information.
:param data: [description]
:return: A 5 tuple of strings containing: Ship ID, Ship Type Name (internal), Ship Name, System, Station, and Value
"""
ships: List[Dict[str, Any]] = companion.listify(cast(list, companion_data.get('ships')))
ships: list[dict[str, Any]] = companion.listify(cast(list, companion_data.get('ships')))
current = companion_data['commander'].get('currentShipId')
if isinstance(current, int) and current < len(ships) and ships[current]:
ships.insert(0, ships.pop(current)) # Put current ship first
if not companion_data['commander'].get('docked'):
out: List[ShipRet] = []
out: list[ShipRet] = []
# Set current system, not last docked
out.append(ShipRet(
id=str(ships[0]['id']),
@@ -285,7 +285,7 @@ def ships(companion_data: Dict[str, Any]) -> List[ShipRet]:
]
def export_ships(companion_data: Dict[str, Any], filename: AnyStr) -> None:
def export_ships(companion_data: dict[str, Any], filename: AnyStr) -> None:
"""
Export the current ships to a CSV file.
@@ -335,8 +335,6 @@ class StatsDialog():
elif (
not capi_data.get('lastSystem')
or not capi_data['lastSystem'].get('name', '').strip()
or not capi_data.get('lastStarport')
or not capi_data['lastStarport'].get('name', '').strip()
):
# Shouldn't happen
# LANG: Unknown location
@@ -358,7 +356,7 @@ class StatsDialog():
class StatsResults(tk.Toplevel):
"""Status window."""
def __init__(self, parent: tk.Tk, data: Dict[str, Any]) -> None:
def __init__(self, parent: tk.Tk, data: dict[str, Any]) -> None:
tk.Toplevel.__init__(self, parent)
self.parent = parent
@@ -442,7 +440,9 @@ class StatsResults(tk.Toplevel):
):
self.geometry(f"+{position.left}+{position.top}")
def addpage(self, parent, header: List[str] = None, align: Optional[str] = None) -> tk.Frame:
def addpage(
self, parent, header: list[str] | None = None, align: str | None = None
) -> ttk.Frame:
"""
Add a page to the StatsResults screen.
@@ -462,7 +462,7 @@ class StatsResults(tk.Toplevel):
return page
def addpageheader(self, parent: tk.Frame, header: Sequence[str], align: Optional[str] = None) -> None:
def addpageheader(self, parent: ttk.Frame, header: Sequence[str], align: str | None = None) -> None:
"""
Add the column headers to the page, followed by a separator.
@@ -478,7 +478,7 @@ class StatsResults(tk.Toplevel):
self.addpagerow(parent, [''])
def addpagerow(
self, parent: tk.Frame, content: Sequence[str], align: Optional[str] = None, with_copy: bool = False
self, parent: ttk.Frame, content: Sequence[str], align: str | None = None, with_copy: bool = False
):
"""
Add a single row to parent.
+5 -4
View File
@@ -1,3 +1,4 @@
# type: ignore
import numbers
import sys
import warnings
@@ -138,7 +139,7 @@ class OldConfig():
self.identifier = f'uk.org.marginal.{appname.lower()}'
NSBundle.mainBundle().infoDictionary()['CFBundleIdentifier'] = self.identifier
self.default_journal_dir = join(
self.default_journal_dir: str | None = join(
NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, True)[0],
'Frontier Developments',
'Elite Dangerous'
@@ -222,13 +223,13 @@ class OldConfig():
journaldir = known_folder_path(FOLDERID_SavedGames)
if journaldir:
self.default_journal_dir = join(journaldir, 'Frontier Developments', 'Elite Dangerous')
self.default_journal_dir: str | None = join(journaldir, 'Frontier Developments', 'Elite Dangerous')
else:
self.default_journal_dir = None
self.identifier = applongname
self.hkey = HKEY()
self.hkey: ctypes.c_void_p | None = HKEY()
disposition = DWORD()
if RegCreateKeyEx(
HKEY_CURRENT_USER,
@@ -376,7 +377,7 @@ class OldConfig():
mkdir(self.plugin_dir)
self.internal_plugin_dir = join(dirname(__file__), 'plugins')
self.default_journal_dir = None
self.default_journal_dir: str | None = None
self.home = expanduser('~')
self.respath = dirname(__file__)
self.identifier = f'uk.org.marginal.{appname.lower()}'
+2 -2
View File
@@ -88,7 +88,7 @@ class TestNewConfig:
if sys.platform != 'linux':
return
from config.linux import LinuxConfig
from config.linux import LinuxConfig # type: ignore
if isinstance(config, LinuxConfig) and config.config is not None:
config.config.read(config.filename)
@@ -179,7 +179,7 @@ class TestOldNewConfig:
if sys.platform != 'linux':
return
from config.linux import LinuxConfig
from config.linux import LinuxConfig # type: ignore
if isinstance(config, LinuxConfig) and config.config is not None:
config.config.read(config.filename)
+21 -22
View File
@@ -3,11 +3,10 @@ import multiprocessing as mp
import os
import pathlib
import sys
from typing import Generator
import pytest
# Import as other names else they get picked up when used as fixtures
from _pytest import monkeypatch as _pytest_monkeypatch
from _pytest import tmpdir as _pytest_tmpdir
from pytest import MonkeyPatch, TempdirFactory, TempPathFactory
from config import config
from journal_lock import JournalLock, JournalLockResult
@@ -117,11 +116,11 @@ class TestJournalLock:
@pytest.fixture
def mock_journaldir(
self, monkeypatch: _pytest_monkeypatch,
tmp_path_factory: _pytest_tmpdir.TempPathFactory
) -> _pytest_tmpdir.TempPathFactory:
self, monkeypatch: MonkeyPatch,
tmp_path_factory: TempdirFactory
) -> Generator:
"""Fixture for mocking config.get_str('journaldir')."""
def get_str(key: str, *, default: str = None) -> str:
def get_str(key: str, *, default: str | None = None) -> str:
"""Mock config.*Config get_str to provide fake journaldir."""
if key == 'journaldir':
return str(tmp_path_factory.getbasetemp())
@@ -136,11 +135,11 @@ class TestJournalLock:
@pytest.fixture
def mock_journaldir_changing(
self,
monkeypatch: _pytest_monkeypatch,
tmp_path_factory: _pytest_tmpdir.TempPathFactory
) -> _pytest_tmpdir.TempPathFactory:
monkeypatch: MonkeyPatch,
tmp_path_factory: TempdirFactory
) -> Generator:
"""Fixture for mocking config.get_str('journaldir')."""
def get_str(key: str, *, default: str = None) -> str:
def get_str(key: str, *, default: str | None = None) -> str:
"""Mock config.*Config get_str to provide fake journaldir."""
if key == 'journaldir':
return tmp_path_factory.mktemp("changing")
@@ -154,7 +153,7 @@ class TestJournalLock:
###########################################################################
# Tests against JournalLock.__init__()
def test_journal_lock_init(self, mock_journaldir: _pytest_tmpdir.TempPathFactory):
def test_journal_lock_init(self, mock_journaldir: TempPathFactory):
"""Test JournalLock instantiation."""
print(f'{type(mock_journaldir)=}')
tmpdir = str(mock_journaldir.getbasetemp())
@@ -176,14 +175,14 @@ class TestJournalLock:
jlock.set_path_from_journaldir()
assert jlock.journal_dir_path is None
def test_path_from_journaldir_with_tmpdir(self, mock_journaldir: _pytest_tmpdir.TempPathFactory):
def test_path_from_journaldir_with_tmpdir(self, mock_journaldir: TempPathFactory):
"""Test JournalLock.set_path_from_journaldir() with tmpdir."""
tmpdir = mock_journaldir
jlock = JournalLock()
# Check that an actual journaldir is handled correctly.
jlock.journal_dir = tmpdir
jlock.journal_dir = str(tmpdir)
jlock.set_path_from_journaldir()
assert isinstance(jlock.journal_dir_path, pathlib.Path)
@@ -200,7 +199,7 @@ class TestJournalLock:
locked = jlock.obtain_lock()
assert locked == JournalLockResult.JOURNALDIR_IS_NONE
def test_obtain_lock_with_tmpdir(self, mock_journaldir: _pytest_tmpdir.TempPathFactory):
def test_obtain_lock_with_tmpdir(self, mock_journaldir: TempPathFactory):
"""Test JournalLock.obtain_lock() with tmpdir."""
jlock = JournalLock()
@@ -213,7 +212,7 @@ class TestJournalLock:
assert jlock.release_lock()
os.unlink(str(jlock.journal_dir_lockfile_name))
def test_obtain_lock_with_tmpdir_ro(self, mock_journaldir: _pytest_tmpdir.TempPathFactory):
def test_obtain_lock_with_tmpdir_ro(self, mock_journaldir: TempPathFactory):
"""Test JournalLock.obtain_lock() with read-only tmpdir."""
tmpdir = str(mock_journaldir.getbasetemp())
print(f'{tmpdir=}')
@@ -280,7 +279,7 @@ class TestJournalLock:
assert locked == JournalLockResult.JOURNALDIR_READONLY
def test_obtain_lock_already_locked(self, mock_journaldir: _pytest_tmpdir.TempPathFactory):
def test_obtain_lock_already_locked(self, mock_journaldir: TempPathFactory):
"""Test JournalLock.obtain_lock() with tmpdir."""
continue_q: mp.Queue = mp.Queue()
exit_q: mp.Queue = mp.Queue()
@@ -312,7 +311,7 @@ class TestJournalLock:
###########################################################################
# Tests against JournalLock.release_lock()
def test_release_lock(self, mock_journaldir: _pytest_tmpdir.TempPathFactory):
def test_release_lock(self, mock_journaldir: TempPathFactory):
"""Test JournalLock.release_lock()."""
# First actually obtain the lock, and check it worked
jlock = JournalLock()
@@ -330,12 +329,12 @@ class TestJournalLock:
# Cleanup, to avoid side-effect on other tests
os.unlink(str(jlock.journal_dir_lockfile_name))
def test_release_lock_not_locked(self, mock_journaldir: _pytest_tmpdir.TempPathFactory):
def test_release_lock_not_locked(self, mock_journaldir: TempPathFactory):
"""Test JournalLock.release_lock() when not locked."""
jlock = JournalLock()
assert jlock.release_lock()
def test_release_lock_lie_locked(self, mock_journaldir: _pytest_tmpdir.TempPathFactory):
def test_release_lock_lie_locked(self, mock_journaldir: TempPathFactory):
"""Test JournalLock.release_lock() when not locked, but lie we are."""
jlock = JournalLock()
jlock.locked = True
@@ -345,7 +344,7 @@ class TestJournalLock:
# Tests against JournalLock.update_lock()
def test_update_lock(
self,
mock_journaldir_changing: _pytest_tmpdir.TempPathFactory):
mock_journaldir_changing: TempPathFactory):
"""
Test JournalLock.update_lock().
@@ -373,7 +372,7 @@ class TestJournalLock:
# And the old_journaldir's lockfile too
os.unlink(str(old_journaldir_lockfile_name))
def test_update_lock_same(self, mock_journaldir: _pytest_tmpdir.TempPathFactory):
def test_update_lock_same(self, mock_journaldir: TempPathFactory):
"""
Test JournalLock.update_lock().
+3 -3
View File
@@ -131,14 +131,14 @@ class _Theme(object):
THEME_TRANSPARENT = 2
def __init__(self) -> None:
self.active = None # Starts out with no theme
self.active: int | None = None # Starts out with no theme
self.minwidth: Optional[int] = None
self.widgets: Dict[tk.Widget | tk.BitmapImage, Set] = {}
self.widgets_pair: List = []
self.defaults: Dict = {}
self.current: Dict = {}
self.default_ui_scale = None # None == not yet known
self.startup_ui_scale = None
self.default_ui_scale: float | None = None # None == not yet known
self.startup_ui_scale: int | None = None
def register(self, widget: tk.Widget | tk.BitmapImage) -> None: # noqa: CCR001, C901
# Note widget and children for later application of a theme. Note if
+3 -1
View File
@@ -25,7 +25,9 @@ class TimeoutAdapter(HTTPAdapter):
return super().send(*args, **kwargs)
def new_session(timeout: int = REQUEST_TIMEOUT, session: requests.Session = None) -> requests.Session:
def new_session(
timeout: int = REQUEST_TIMEOUT, session: requests.Session | None = None
) -> requests.Session:
"""
Create a new requests.Session and override the default HTTPAdapter with a TimeoutAdapter.
+2 -2
View File
@@ -19,7 +19,7 @@ import tkinter as tk
import webbrowser
from tkinter import font as tk_font
from tkinter import ttk
from typing import TYPE_CHECKING, Any, Optional
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
def _(x: str) -> str: ...
@@ -29,7 +29,7 @@ if TYPE_CHECKING:
class HyperlinkLabel(sys.platform == 'darwin' and tk.Label or ttk.Label, object): # type: ignore
"""Clickable label for HTTP links."""
def __init__(self, master: Optional[tk.Tk] = None, **kw: Any) -> None:
def __init__(self, master: tk.Frame | None = None, **kw: Any) -> None:
self.url = 'url' in kw and kw.pop('url') or None
self.popup_copy = kw.pop('popup_copy', False)
self.underline = kw.pop('underline', None) # override ttk.Label's underline
+3
View File
@@ -221,6 +221,9 @@ class Updater(object):
status['text'] = newversion.title + ' is available'
self.root.update_idletasks()
else:
logger.info("No new version available at this time")
def close(self) -> None:
"""
Handle the EDMarketConnector.AppWindow.onexit() request.