diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index 4504130a1..dacdcc3fb 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -42,10 +42,13 @@ from eos.saveddata.targetProfile import TargetProfile from eos.utils.float import floatUnerr from eos.utils.stats import DmgTypes, RRTypes - pyfalog = Logger(__name__) +def _t(x): + return x + + class FitLite: def __init__(self, id=None, name=None, shipID=None, shipName=None, shipNameShort=None): @@ -395,16 +398,16 @@ class Fit: @property def scanType(self): maxStr = -1 - type = None - for scanType in ("Magnetometric", "Ladar", "Radar", "Gravimetric"): + type_ = None + for scanType in (_t("Magnetometric"), _t("Ladar"), _t("Radar"), _t("Gravimetric")): currStr = self.ship.getModifiedItemAttr("scan%sStrength" % scanType) if currStr > maxStr: maxStr = currStr - type = scanType + type_ = scanType elif currStr == maxStr: - type = "Multispectral" + type_ = _t("Multispectral") - return type + return type_ @property def jamChance(self): @@ -443,9 +446,9 @@ class Fit: @validates("ID", "ownerID", "shipID") def validator(self, key, val): map = { - "ID" : lambda _val: isinstance(_val, int), + "ID": lambda _val: isinstance(_val, int), "ownerID": lambda _val: isinstance(_val, int) or _val is None, - "shipID" : lambda _val: isinstance(_val, int) or _val is None + "shipID": lambda _val: isinstance(_val, int) or _val is None } if not map[key](val): @@ -578,15 +581,15 @@ class Fit: if warfareBuffID == 11: # Shield Burst: Active Shielding: Repair Duration/Capacitor self.modules.filteredItemBoost( - lambda mod: mod.item.requiresSkill("Shield Operation") or - mod.item.requiresSkill("Shield Emission Systems") or - mod.item.requiresSkill("Capital Shield Emission Systems"), - "capacitorNeed", value) + lambda mod: mod.item.requiresSkill("Shield Operation") or + mod.item.requiresSkill("Shield Emission Systems") or + mod.item.requiresSkill("Capital Shield Emission Systems"), + "capacitorNeed", value) self.modules.filteredItemBoost( - lambda mod: mod.item.requiresSkill("Shield Operation") or - mod.item.requiresSkill("Shield Emission Systems") or - mod.item.requiresSkill("Capital Shield Emission Systems"), - "duration", value) + lambda mod: mod.item.requiresSkill("Shield Operation") or + mod.item.requiresSkill("Shield Emission Systems") or + mod.item.requiresSkill("Capital Shield Emission Systems"), + "duration", value) if warfareBuffID == 12: # Shield Burst: Shield Extension: Shield HP self.ship.boostItemAttr("shieldCapacity", value, stackingPenalties=True) @@ -597,15 +600,15 @@ class Fit: if warfareBuffID == 14: # Armor Burst: Rapid Repair: Repair Duration/Capacitor self.modules.filteredItemBoost( - lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or - mod.item.requiresSkill("Repair Systems") or - mod.item.requiresSkill("Capital Remote Armor Repair Systems"), - "capacitorNeed", value) + lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or + mod.item.requiresSkill("Repair Systems") or + mod.item.requiresSkill("Capital Remote Armor Repair Systems"), + "capacitorNeed", value) self.modules.filteredItemBoost( - lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or - mod.item.requiresSkill("Repair Systems") or - mod.item.requiresSkill("Capital Remote Armor Repair Systems"), - "duration", value) + lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or + mod.item.requiresSkill("Repair Systems") or + mod.item.requiresSkill("Capital Remote Armor Repair Systems"), + "duration", value) if warfareBuffID == 15: # Armor Burst: Armor Reinforcement: Armor HP self.ship.boostItemAttr("armorHP", value, stackingPenalties=True) @@ -980,14 +983,14 @@ class Fit: for _ in range(projectionInfo.amount): targetFit.register(item, origin=self) item.calculateModifiedAttributes( - targetFit, runTime, forceProjected=True, - forcedProjRange=0) + targetFit, runTime, forceProjected=True, + forcedProjRange=0) for mod in self.modules: for _ in range(projectionInfo.amount): targetFit.register(mod, origin=self) mod.calculateModifiedAttributes( - targetFit, runTime, forceProjected=True, - forcedProjRange=projectionInfo.projectionRange) + targetFit, runTime, forceProjected=True, + forcedProjRange=projectionInfo.projectionRange) def fill(self): """ @@ -1003,7 +1006,8 @@ class Fit: # Look for any dummies of that type to remove posToRemove = {} - for slotType in (FittingSlot.LOW.value, FittingSlot.MED.value, FittingSlot.HIGH.value, FittingSlot.RIG.value, FittingSlot.SUBSYSTEM.value, FittingSlot.SERVICE.value): + for slotType in ( + FittingSlot.LOW.value, FittingSlot.MED.value, FittingSlot.HIGH.value, FittingSlot.RIG.value, FittingSlot.SUBSYSTEM.value, FittingSlot.SERVICE.value): amount = self.getSlotsFree(slotType, True) if amount > 0: for _ in range(int(amount)): @@ -1080,22 +1084,23 @@ class Fit: for mod in chain(self.modules, self.fighters): if mod.slot is type and (not getattr(mod, "isEmpty", False) or countDummies): - if type in (FittingSlot.F_HEAVY, FittingSlot.F_SUPPORT, FittingSlot.F_LIGHT, FittingSlot.FS_HEAVY, FittingSlot.FS_LIGHT, FittingSlot.FS_SUPPORT) and not mod.active: + if type in (FittingSlot.F_HEAVY, FittingSlot.F_SUPPORT, FittingSlot.F_LIGHT, FittingSlot.FS_HEAVY, FittingSlot.FS_LIGHT, + FittingSlot.FS_SUPPORT) and not mod.active: continue amount += 1 return amount slots = { - FittingSlot.LOW : "lowSlots", - FittingSlot.MED : "medSlots", - FittingSlot.HIGH : "hiSlots", - FittingSlot.RIG : "rigSlots", + FittingSlot.LOW: "lowSlots", + FittingSlot.MED: "medSlots", + FittingSlot.HIGH: "hiSlots", + FittingSlot.RIG: "rigSlots", FittingSlot.SUBSYSTEM: "maxSubSystems", - FittingSlot.SERVICE : "serviceSlots", - FittingSlot.F_LIGHT : "fighterLightSlots", + FittingSlot.SERVICE: "serviceSlots", + FittingSlot.F_LIGHT: "fighterLightSlots", FittingSlot.F_SUPPORT: "fighterSupportSlots", - FittingSlot.F_HEAVY : "fighterHeavySlots", + FittingSlot.F_HEAVY: "fighterHeavySlots", FittingSlot.FS_LIGHT: "fighterStandupLightSlots", FittingSlot.FS_SUPPORT: "fighterStandupSupportSlots", FittingSlot.FS_HEAVY: "fighterStandupHeavySlots", @@ -1377,8 +1382,8 @@ class Fit: """Return how much cap regen do we gain from having this module""" currentRegen = self.calculateCapRecharge() nomodRegen = self.calculateCapRecharge( - capacity=self.ship.getModifiedItemAttrExtended("capacitorCapacity", ignoreAfflictors=[mod]), - rechargeRate=self.ship.getModifiedItemAttrExtended("rechargeRate", ignoreAfflictors=[mod]) / 1000.0) + capacity=self.ship.getModifiedItemAttrExtended("capacitorCapacity", ignoreAfflictors=[mod]), + rechargeRate=self.ship.getModifiedItemAttrExtended("rechargeRate", ignoreAfflictors=[mod]) / 1000.0) return currentRegen - nomodRegen def getRemoteReps(self, spoolOptions=None): @@ -1422,7 +1427,8 @@ class Fit: "armorRepair": self.extraAttributes["armorRepair"], "armorRepairPreSpool": self.extraAttributes["armorRepairPreSpool"], "armorRepairFullSpool": self.extraAttributes["armorRepairFullSpool"], - "hullRepair": self.extraAttributes["hullRepair"]} + "hullRepair": self.extraAttributes["hullRepair"] + } return reps @property @@ -1462,7 +1468,8 @@ class Fit: "armorRepair": self.extraAttributes["armorRepair"], "armorRepairPreSpool": self.extraAttributes["armorRepairPreSpool"], "armorRepairFullSpool": self.extraAttributes["armorRepairFullSpool"], - "hullRepair": self.extraAttributes["hullRepair"]} + "hullRepair": self.extraAttributes["hullRepair"] + } if not self.capStable or self.factorReload: # Map a local repairer type to the attribute it uses groupAttrMap = { @@ -1470,14 +1477,16 @@ class Fit: "Ancillary Shield Booster": "shieldBonus", "Armor Repair Unit": "armorDamageAmount", "Ancillary Armor Repairer": "armorDamageAmount", - "Hull Repair Unit": "structureDamageAmount"} + "Hull Repair Unit": "structureDamageAmount" + } # Map local repairer type to tank type groupStoreMap = { "Shield Booster": "shieldRepair", "Ancillary Shield Booster": "shieldRepair", "Armor Repair Unit": "armorRepair", "Ancillary Armor Repairer": "armorRepair", - "Hull Repair Unit": "hullRepair"} + "Hull Repair Unit": "hullRepair" + } repairers = [] localAdjustment = {"shieldRepair": 0, "armorRepair": 0, "hullRepair": 0} capUsed = self.capUsed @@ -1529,7 +1538,7 @@ class Fit: # Sort repairers by efficiency. We want to use the most efficient repairers first repairers.sort(key=lambda _mod: _mod.getModifiedItemAttr( - groupAttrMap[_mod.item.group.name]) * (_mod.getModifiedItemAttr( + groupAttrMap[_mod.item.group.name]) * (_mod.getModifiedItemAttr( "chargedArmorDamageMultiplier") or 1) / _mod.getModifiedItemAttr("capacitorNeed"), reverse=True) # Loop through every module until we're above peak recharge @@ -1654,7 +1663,6 @@ class Fit: secstatus = FitSystemSecurity.NULLSEC return secstatus - def activeModulesIter(self): for mod in self.modules: if mod.state >= FittingModuleState.ACTIVE: diff --git a/eos/utils/stats.py b/eos/utils/stats.py index 76ed3edeb..5b6329c16 100644 --- a/eos/utils/stats.py +++ b/eos/utils/stats.py @@ -22,6 +22,10 @@ from eos.utils.float import floatUnerr from utils.repr import makeReprStr +def _t(x): + return x + + class DmgTypes: """Container for damage data stats.""" @@ -116,7 +120,7 @@ class DmgTypes: @staticmethod def names(short=None, postProcessor=None): - value = ['em', 'th', 'kin', 'exp'] if short else ['em', 'thermal', 'kinetic', 'explosive'] + value = [_t('em'), _t('th'), _t('kin'), _t('exp')] if short else [_t('em'), _t('thermal'), _t('kinetic'), _t('explosive')] if postProcessor: value = [postProcessor(x) for x in value] diff --git a/gui/builtinContextMenus/itemRemove.py b/gui/builtinContextMenus/itemRemove.py index b0b875862..8e8908b80 100644 --- a/gui/builtinContextMenus/itemRemove.py +++ b/gui/builtinContextMenus/itemRemove.py @@ -39,9 +39,9 @@ class RemoveItem(ContextMenuCombined): return True def getText(self, callingWindow, itmContext, mainItem, selection): - return _t('Remove {}{}').format( - itmContext if itmContext is not None else _t('Item'), - _t(' Stack') if self.srcContext in ('droneItem', 'projectedDrone', 'cargoItem', 'projectedFit') else '') + return _t('Remove {item}{stack}').format( + item=itmContext if itmContext is not None else _t('Item'), + stack=_t(' Stack') if self.srcContext in ('droneItem', 'projectedDrone', 'cargoItem', 'projectedFit') else '') def activate(self, callingWindow, fullContext, mainItem, selection, i): handlerMap = { diff --git a/gui/builtinStatsViews/capacitorViewFull.py b/gui/builtinStatsViews/capacitorViewFull.py index 9ba2a0cf5..dd89ed5b0 100644 --- a/gui/builtinStatsViews/capacitorViewFull.py +++ b/gui/builtinStatsViews/capacitorViewFull.py @@ -136,10 +136,9 @@ class CapacitorViewFull(StatsView): label.SetToolTip(wx.ToolTip("%.1f" % value)) if labelName == 'label%sCapacitorDelta' and (cap_recharge or cap_use): - lines = [] - lines.append('Capacitor delta:') - lines.append(' +{} GJ/s'.format(formatAmount(cap_recharge, 3, 0, 3))) - lines.append(' -{} GJ/s'.format(formatAmount(cap_use, 3, 0, 3))) + lines = [_t('Capacitor delta:'), + ' +{} GJ/s'.format(formatAmount(cap_recharge, 3, 0, 3)), + ' -{} GJ/s'.format(formatAmount(cap_use, 3, 0, 3))] delta = round(cap_recharge - cap_use, 3) if delta > 0 and 0 < round(neut_res, 4) < 1: lines.append('') @@ -147,9 +146,9 @@ class CapacitorViewFull(StatsView): lines.append(' +{} GJ/s'.format(formatAmount(delta / neut_res, 3, 0, 3))) label.SetToolTip(wx.ToolTip('\n'.join(lines))) if labelName == 'label%sCapacitorResist': - texts = ['Neutralizer resistance'] + texts = [_t('Neutralizer resistance')] if cap_amount > 0 and 0 < round(neut_res, 4) < 1: - texts.append('Effective capacity: {} GJ'.format(formatAmount(cap_amount / neut_res, 3, 0, 9))) + texts.append(_t('Effective capacity') + ': {} GJ'.format(formatAmount(cap_amount / neut_res, 3, 0, 9))) label.SetToolTip(wx.ToolTip('\n'.join(texts))) capState = fit.capState if fit is not None else 0 diff --git a/gui/builtinStatsViews/firepowerViewFull.py b/gui/builtinStatsViews/firepowerViewFull.py index 62ae6cc2a..8540accc6 100644 --- a/gui/builtinStatsViews/firepowerViewFull.py +++ b/gui/builtinStatsViews/firepowerViewFull.py @@ -166,20 +166,20 @@ class FirepowerViewFull(StatsView): hasSpool = hasSpoolUp(preSpool, fullSpool) lines = [] if hasSpool: - lines.append("Spool up: {}-{}".format( - formatAmount(preSpool.total, prec, lowest, highest), - formatAmount(fullSpool.total, prec, lowest, highest))) + lines.append(_t("Spool up") + ": {}-{}".format( + formatAmount(preSpool.total, prec, lowest, highest), + formatAmount(fullSpool.total, prec, lowest, highest))) if getattr(normal, 'total', None): if hasSpool: lines.append("") - lines.append("Current: {}".format(formatAmount(normal.total, prec, lowest, highest))) + lines.append(_t("Current") + ": {}".format(formatAmount(normal.total, prec, lowest, highest))) for dmgType in normal.names(): val = getattr(normal, dmgType, None) if val: lines.append("{}{}: {}%".format( - " " if hasSpool else "", - dmgType.capitalize(), - formatAmount(val / normal.total * 100, 3, 0, 0))) + " " if hasSpool else "", + _t(dmgType).capitalize(), + formatAmount(val / normal.total * 100, 3, 0, 0))) return "\n".join(lines) defaultSpoolValue = eos.config.settings['globalDefaultSpoolupPercentage'] @@ -218,8 +218,8 @@ class FirepowerViewFull(StatsView): if self._cachedValues[counter] != val: tooltipText = dpsToolTip(val, preSpoolVal, fullSpoolVal, prec, lowest, highest) label.SetLabel(valueFormat.format( - formatAmount(0 if val is None else val.total, prec, lowest, highest), - "\u02e2" if hasSpoolUp(preSpoolVal, fullSpoolVal) else "")) + formatAmount(0 if val is None else val.total, prec, lowest, highest), + "\u02e2" if hasSpoolUp(preSpoolVal, fullSpoolVal) else "")) label.SetToolTip(wx.ToolTip(tooltipText)) self._cachedValues[counter] = val counter += 1 diff --git a/gui/builtinStatsViews/outgoingViewFull.py b/gui/builtinStatsViews/outgoingViewFull.py index 8b214c052..655cee084 100644 --- a/gui/builtinStatsViews/outgoingViewFull.py +++ b/gui/builtinStatsViews/outgoingViewFull.py @@ -29,25 +29,25 @@ _t = wx.GetTranslation stats = [ ( - "labelRemoteCapacitor", "Capacitor:", "{}{} GJ/s", "capacitorInfo", "Capacitor restored", + "labelRemoteCapacitor", "Capacitor:", "{}{} GJ/s", "capacitorInfo", _t("Capacitor restored"), lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, spool, False)).capacitor, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 0, True)).capacitor, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 1, True)).capacitor, 3, 0, 0), ( - "labelRemoteShield", "Shield:", "{}{} HP/s", "shieldActive", "Shield restored", + "labelRemoteShield", "Shield:", "{}{} HP/s", "shieldActive", _t("Shield restored"), lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, spool, False)).shield, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 0, True)).shield, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 1, True)).shield, 3, 0, 0), ( - "labelRemoteArmor", "Armor:", "{}{} HP/s", "armorActive", "Armor restored", + "labelRemoteArmor", "Armor:", "{}{} HP/s", "armorActive", _t("Armor restored"), lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, spool, False)).armor, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 0, True)).armor, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 1, True)).armor, 3, 0, 0), ( - "labelRemoteHull", "Hull:", "{}{} HP/s", "hullActive", "Hull restored", + "labelRemoteHull", "Hull:", "{}{} HP/s", "hullActive", _t("Hull restored"), lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, spool, False)).hull, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 0, True)).hull, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 1, True)).hull, diff --git a/gui/builtinStatsViews/outgoingViewMinimal.py b/gui/builtinStatsViews/outgoingViewMinimal.py index 9ce5439a8..17f1b0a44 100644 --- a/gui/builtinStatsViews/outgoingViewMinimal.py +++ b/gui/builtinStatsViews/outgoingViewMinimal.py @@ -28,25 +28,25 @@ _t = wx.GetTranslation stats = [ ( - "labelRemoteCapacitor", "Capacitor:", "{}{} GJ/s", "capacitorInfo", "Capacitor restored", + "labelRemoteCapacitor", "Capacitor:", "{}{} GJ/s", "capacitorInfo", _t("Capacitor restored"), lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, spool, False)).capacitor, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 0, True)).capacitor, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 1, True)).capacitor, 3, 0, 0), ( - "labelRemoteShield", "Shield:", "{}{} HP/s", "shieldActive", "Shield restored", + "labelRemoteShield", "Shield:", "{}{} HP/s", "shieldActive", _t("Shield restored"), lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, spool, False)).shield, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 0, True)).shield, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 1, True)).shield, 3, 0, 0), ( - "labelRemoteArmor", "Armor:", "{}{} HP/s", "armorActive", "Armor restored", + "labelRemoteArmor", "Armor:", "{}{} HP/s", "armorActive", _t("Armor restored"), lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, spool, False)).armor, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 0, True)).armor, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 1, True)).armor, 3, 0, 0), ( - "labelRemoteHull", "Hull:", "{}{} HP/s", "hullActive", "Hull restored", + "labelRemoteHull", "Hull:", "{}{} HP/s", "hullActive", _t("Hull restored"), lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, spool, False)).hull, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 0, True)).hull, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 1, True)).hull, diff --git a/gui/builtinStatsViews/targetingMiscViewMinimal.py b/gui/builtinStatsViews/targetingMiscViewMinimal.py index 8a0f023e5..99ac46169 100644 --- a/gui/builtinStatsViews/targetingMiscViewMinimal.py +++ b/gui/builtinStatsViews/targetingMiscViewMinimal.py @@ -215,11 +215,11 @@ class TargetingMiscViewMinimal(StatsView): ecmChance = otherValues["jamChance"] ecmChance = round(ecmChance, 1) if ecmChance > 0: - label.SetToolTip(wx.ToolTip(_t("Type: {0}\n").foramt(fit.scanType) + + label.SetToolTip(wx.ToolTip(_t("Type: {0}\n").foramt(_t(fit.scanType)) + # xgettext:no-python-format,python-brace-format _t("{}% chance to be jammed").format(formatAmount(ecmChance, 3, 0, 0)))) else: - label.SetToolTip(wx.ToolTip(_t("Type: {}").format(fit.scanType))) + label.SetToolTip(wx.ToolTip(_t("Type: {}").format(_t(fit.scanType)))) elif labelName == "labelFullAlignTime": alignTime = _t("Align:\t%.3fs") % mainValue mass = _t('Mass:\t{:,.0f}kg').format(fit.ship.getModifiedItemAttr("mass")) diff --git a/locale/README.md b/locale/README.md index ce28d2ab1..3a8cf3196 100644 --- a/locale/README.md +++ b/locale/README.md @@ -6,12 +6,12 @@ pyfa provides community-driven translations for a variety of languages. It is im * Ship browser * Item names, description, traits, attributes -If there is a tranlation issue in EVE data, you must submit a ticket to CCP instead. +If there is a translation issue in EVE data, you must submit a ticket to CCP instead. ## Getting Involved Translations are done mainly through [Crowdin](https://crowdin.com/project/pyfa). This platform allows translations to be done by anyone without any real need to understand the project's internals. Simply sign up, join the project as a Translator, and start translating! - + As a general rule of thumb, we consider translations community-driven. The pyfa team isn't going to 1) Maintain individual language packs as a part of general development work, or @@ -42,13 +42,13 @@ msgstr "点击切换有效HP和原始HP" [Poedit](https://poedit.net/) offers a nice GUI for updating translations. -##### To update PO file for existing translation +#### To update PO file for existing translation 1. open a existing `locale/ll_CC/LC_MESSAGES/lang.po` 2. *Catalog* -> *Update from POT file* 3. select pre-prepared `lang.pot` file -##### To translate and generate MO file +#### To translate and generate MO file edit the translation and hit Save :) @@ -59,7 +59,8 @@ A: This is probably one of two things: 1. Missing annotations in the source code. All text that needs to be translated needs to be wrapped with `_t()` to make it locale-aware 2. Out of date `.po` file. As pyfa development continues, the `.po` file may fall behind. See next question. - + + Q: How do I update the `.po` file for my language?
A: See `Commands` section below for a number of useful commands @@ -70,31 +71,32 @@ A: If you're running from source / your own method, this is because the `.mo` fi Below is a summary of [GNU gettext](https://www.gnu.org/software/gettext/) manual, adapted for Pyfa i18n workflow. -[Poedit](https://poedit.net/) offers a nice GUI for same GNU gettext translation workflow. - -## i18n with command line - -Windows users can get these tools via Git for windows, Msys2 or Cygwin; or just use WSL / WSL2. -For Linux and macOS users these tools might be available out-of-box. +Windows users can get these tools via Git for windows, Msys2 or Cygwin; or just use WSL / WSL2. For Linux and macOS users these tools might be available out-of-box. ### To generate new template for translation: ```console -$ find gui/ *.py -name "*.py" | xgettext --from-code=UTF-8 -o locale/lang.pot -d lang -k_t -f - -s +$ find * -name "*.py" | xgettext --from-code=UTF-8 -o locale/lang.pot -d lang -k_t -k_t:1,2,3t -k_t:1,2c,2t -f - -s ``` explanation: -* `find gui/ *.py -name "*.py"`: collect all `.py` file path in `gui` folder and all sub-folders, write it to stdout -* `xgettext`: a utility looking for keyword and put string literals in a specific format for human translation +* `find * -name "*.py"`: collect all `.py` file path in current folder and all sub-folders, write it to stdout + * except those starts with `.`. E.g. `.env`, `.idea`, `.venv`. + * can also append `-not -path 'path/to/venv/*` to exclude `path/to/venv` recursively. + +* `xgettext` ([doc](https://www.gnu.org/software/gettext/manual/gettext.html#Template)): a utility looking for keyword and put string literals in a specific format for human translation * `--from-code=UTF-8`: designates encoding of files * `-o locale/lang.pot`: let `xgettext` write to `locale/lang.pot` * `-d lang`: default language domain is `lang` - * `-k_t`: besides default keyword (including `_`, see `info xgettext` for detail), also look for `_t` - * `-f -`: let `xgettext` to read from stdin, which is connected to `find` stdout + * `-k_t`: besides default keyword (including `_`, see `info xgettext` for detail), also look for `_t`, + where the string literal (`msgid`) will be the first argument of this function call + * `-k_t:1,2,3t`: look for `_t`, first arg is `msgid`, second arg is `msgid_plural`, 3 args in total + * `-k_t:1,2c,2t`: look for `_t`, first arg is `msgid`, second arg is `msgctxt`, 2 args in total + * `-f -`: let `xgettext` to read filenames from stdin, which is connected to `find` stdout * `-s`: sort output according to `msgid` -this `locale/lang.pot` is called PO template, which is discarded once actual `ll_CC/LC_MESSAGES/lang.po` is ready for use. +this `locale/lang.pot` is called PO template, which is the source file for Crowdin translation. ### To initialize PO file for new language diff --git a/locale/lang.pot b/locale/lang.pot index 260871d3e..e234bbc99 100644 --- a/locale/lang.pot +++ b/locale/lang.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-07-17 11:42+0800\n" +"POT-Creation-Date: 2020-07-27 15:29+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -16,6 +16,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #: gui/builtinStatsViews/firepowerViewFull.py:107 msgid " DPS: " @@ -32,7 +33,9 @@ msgstr "" #: gui/builtinItemStatsViews/itemProperties.py:98 #, python-format msgid "%d attribute." -msgstr "" +msgid_plural "%d attributes." +msgstr[0] "" +msgstr[1] "" #: gui/mainMenuBar.py:73 msgid "&Backup All Fittings" @@ -276,6 +279,10 @@ msgstr "" msgid "Add Fits" msgstr "" +#: gui/builtinContextMenus/targetProfile/adder.py:29 +msgid "Add Target Profile" +msgstr "" + #: gui/builtinContextMenus/cargoAddAmmo.py:28 #, python-brace-format msgid "Add {0} to Cargo (x1000)" @@ -370,7 +377,7 @@ msgstr "" msgid "Angel Cartel" msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:70 +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:73 msgid "Animate gauges" msgstr "" @@ -420,6 +427,11 @@ msgstr "" msgid "Armor resistance" msgstr "" +#: gui/builtinStatsViews/outgoingViewFull.py:44 +#: gui/builtinStatsViews/outgoingViewMinimal.py:43 +msgid "Armor restored" +msgstr "" + #: gui/builtinStatsViews/resistancesViewFull.py:206 msgid "Armor: " msgstr "" @@ -462,6 +474,11 @@ msgstr "" msgid "Auto" msgstr "" +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:145 +msgid "" +"Auto will use the same language pyfa uses if available, otherwise English" +msgstr "" + #: gui/builtinPreferenceViews/pyfaNetworkPreferences.py:87 msgid "Auto-detected proxy settings" msgstr "" @@ -622,6 +639,15 @@ msgstr "" msgid "Capacitor" msgstr "" +#: gui/builtinStatsViews/capacitorViewFull.py:139 +msgid "Capacitor delta:" +msgstr "" + +#: gui/builtinStatsViews/outgoingViewFull.py:32 +#: gui/builtinStatsViews/outgoingViewMinimal.py:31 +msgid "Capacitor restored" +msgstr "" + #: gui/builtinStatsViews/capacitorViewFull.py:57 msgid "Capacitor stability" msgstr "" @@ -678,7 +704,7 @@ msgstr "" msgid "Change Skills" msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:81 +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:84 msgid "Change charge in all modules of the same type" msgstr "" @@ -761,7 +787,7 @@ msgstr "" msgid "Client Secret:" msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:51 +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:54 msgid "Color fitting view by slot" msgstr "" @@ -785,7 +811,7 @@ msgstr "" msgid "Command center hold" msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:47 +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:50 msgid "Compact skills needed tooltip" msgstr "" @@ -902,6 +928,10 @@ msgstr "" msgid "Cruor (Blood Raiders)" msgstr "" +#: gui/builtinStatsViews/firepowerViewFull.py:175 +msgid "Current" +msgstr "" + #: gui/builtinItemStatsViews/itemAttributes.py:45 #: gui/builtinItemStatsViews/itemProperties.py:57 msgid "Current Value" @@ -1131,7 +1161,7 @@ msgstr "" msgid "Drag a fit to this area" msgstr "" -#: gui/builtinAdditionPanes/projectedView.py:226 +#: gui/builtinAdditionPanes/projectedView.py:224 msgid "Drag an item or fit, or use right-click menu for wormhole effects" msgstr "" @@ -1240,6 +1270,10 @@ msgstr "" msgid "EVE API XML character files" msgstr "" +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:127 +msgid "EVE Data:" +msgstr "" + #: gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py:49 msgid "EVE IGB HTML fitting file" msgstr "" @@ -1278,6 +1312,10 @@ msgstr "" msgid "Effective HP: " msgstr "" +#: gui/builtinStatsViews/capacitorViewFull.py:151 +msgid "Effective capacity" +msgstr "" + #: graphs/data/fitDamageStats/graph.py:85 msgid "Effective damage inflicted" msgstr "" @@ -1558,7 +1596,7 @@ msgstr "" msgid "Exporting skills needed..." msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:89 +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:92 msgid "Extra info in Additions panel tab names" msgstr "" @@ -1758,7 +1796,7 @@ msgstr "" msgid "Gas hold" msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:17 +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:20 msgid "General" msgstr "" @@ -1795,6 +1833,10 @@ msgstr "" msgid "Graphs" msgstr "" +#: eos/saveddata/fit.py:402 +msgid "Gravimetric" +msgstr "" + #: graphs/style.py:53 msgid "Green" msgstr "" @@ -1861,6 +1903,11 @@ msgstr "" msgid "Hull resistance" msgstr "" +#: gui/builtinStatsViews/outgoingViewFull.py:50 +#: gui/builtinStatsViews/outgoingViewMinimal.py:49 +msgid "Hull restored" +msgstr "" + #: gui/builtinStatsViews/resistancesViewFull.py:206 msgid "Hull: " msgstr "" @@ -2025,6 +2072,10 @@ msgstr "" msgid "Interceptor" msgstr "" +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:122 +msgid "Interested in helping with translations?" +msgstr "" + #: eos/saveddata/damagePattern.py:127 eos/saveddata/damagePattern.py:128 #: eos/saveddata/damagePattern.py:132 eos/saveddata/damagePattern.py:135 #: eos/saveddata/damagePattern.py:138 eos/saveddata/targetProfile.py:94 @@ -2092,8 +2143,12 @@ msgstr "" msgid "Kinetic resistance" msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:96 -msgid "Language (restart required):" +#: eos/saveddata/fit.py:402 +msgid "Ladar" +msgstr "" + +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:97 +msgid "Language (requires restart)" msgstr "" #: gui/builtinStatsViews/targetingMiscViewMinimal.py:120 @@ -2101,7 +2156,7 @@ msgid "Large ship hold" msgstr "" #: gui/builtinStatsViews/capacitorViewFull.py:77 -#: gui/builtinStatsViews/capacitorViewFull.py:171 +#: gui/builtinStatsViews/capacitorViewFull.py:170 msgid "Lasts " msgstr "" @@ -2208,6 +2263,10 @@ msgstr "" msgid "Magenta" msgstr "" +#: eos/saveddata/fit.py:402 +msgid "Magnetometric" +msgstr "" + #: gui/builtinStatsViews/targetingMiscViewMinimal.py:114 msgid "Maintenance bay" msgstr "" @@ -2378,6 +2437,10 @@ msgstr "" msgid "Multifrequency" msgstr "" +#: eos/saveddata/fit.py:408 +msgid "Multispectral" +msgstr "" + #: gui/copySelectDialog.py:54 msgid "Mutated Attributes" msgstr "" @@ -2497,6 +2560,10 @@ msgstr "" msgid "Network" msgstr "" +#: gui/builtinStatsViews/capacitorViewFull.py:149 +msgid "Neutralizer resistance" +msgstr "" + #: graphs/data/fitEwarStats/graph.py:36 msgid "Neuts: cap per second" msgstr "" @@ -2540,7 +2607,7 @@ msgstr "" msgid "No proxy" msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:90 +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:93 #: gui/builtinPreferenceViews/pyfaStatViewPreferences.py:42 #: gui/builtinPreferenceViews/pyfaStatViewPreferences.py:50 #: gui/builtinPreferenceViews/pyfaStatViewPreferences.py:58 @@ -2613,7 +2680,7 @@ msgstr "" msgid "Open Widgets Inspect tool" msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:73 +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:76 msgid "Open fittings in a new page by default" msgstr "" @@ -2769,10 +2836,10 @@ msgstr "" msgid "Projected" msgstr "" -#: gui/builtinAdditionPanes/projectedView.py:306 -#: gui/builtinAdditionPanes/projectedView.py:316 -#: gui/builtinAdditionPanes/projectedView.py:321 -#: gui/builtinAdditionPanes/projectedView.py:326 +#: gui/builtinAdditionPanes/projectedView.py:304 +#: gui/builtinAdditionPanes/projectedView.py:314 +#: gui/builtinAdditionPanes/projectedView.py:319 +#: gui/builtinAdditionPanes/projectedView.py:324 msgid "Projected Item" msgstr "" @@ -2832,14 +2899,18 @@ msgstr "" msgid "Quafe hold" msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:90 +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:93 msgid "Quantity of active items" msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:90 +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:93 msgid "Quantity of all items" msgstr "" +#: eos/saveddata/fit.py:402 +msgid "Radar" +msgstr "" + #: eos/saveddata/damagePattern.py:45 msgid "Radio" msgstr "" @@ -2929,14 +3000,15 @@ msgid "Remove Overides for Item" msgstr "" #: gui/builtinContextMenus/itemRemove.py:42 -msgid "Remove {}{}" +#, python-brace-format +msgid "Remove {item}{stack}" msgstr "" #: gui/builtinShipBrowser/fitItem.py:108 msgid "Rename" msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:55 +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:58 msgid "Reopen previous fits on startup" msgstr "" @@ -3040,6 +3112,10 @@ msgstr "" msgid "Salvage hold" msgstr "" +#: locale_test/getTextLocale.py:4 +msgid "Sample Title Text English" +msgstr "" + #: eos/saveddata/damagePattern.py:156 eos/saveddata/targetProfile.py:80 #: gui/builtinContextMenus/envEffectAdd.py:112 msgid "Sansha Incursion" @@ -3130,7 +3206,7 @@ msgstr "" msgid "Sentinel" msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:59 +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:62 msgid "Separate Racks" msgstr "" @@ -3200,6 +3276,11 @@ msgstr "" msgid "Shield resistance" msgstr "" +#: gui/builtinStatsViews/outgoingViewFull.py:38 +#: gui/builtinStatsViews/outgoingViewMinimal.py:37 +msgid "Shield restored" +msgstr "" + #: gui/builtinPreferenceViews/pyfaStatViewPreferences.py:58 msgid "Shield/Armor Tank" msgstr "" @@ -3230,7 +3311,7 @@ msgstr "" msgid "Short Range" msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:63 +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:66 msgid "Show Rack Labels" msgstr "" @@ -3238,7 +3319,7 @@ msgstr "" msgid "Show empty ship groups" msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:67 +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:70 msgid "Show fitting tab tooltips" msgstr "" @@ -3250,7 +3331,7 @@ msgstr "" msgid "Show market shortcuts" msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:77 +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:80 msgid "Show ship browser tooltip" msgstr "" @@ -3329,6 +3410,10 @@ msgstr "" msgid "Split {} Stack" msgstr "" +#: gui/builtinStatsViews/firepowerViewFull.py:169 +msgid "Spool up" +msgstr "" + #: gui/builtinPreferenceViews/pyfaContextMenuPreferences.py:76 msgid "Spoolup" msgstr "" @@ -3337,7 +3422,7 @@ msgstr "" msgid "Spoolup Cycles" msgstr "" -#: gui/builtinStatsViews/capacitorViewFull.py:171 +#: gui/builtinStatsViews/capacitorViewFull.py:170 msgid "Stable: " msgstr "" @@ -3768,15 +3853,15 @@ msgstr "" msgid "Use capacitor simulator" msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:39 +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:42 msgid "Use character implants by default for new fits" msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:35 +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:38 msgid "Use global character" msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:43 +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:46 msgid "Use global damage pattern" msgstr "" @@ -3847,7 +3932,7 @@ msgid "" "behavior)." msgstr "" -#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:86 +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:89 msgid "" "When disabled, reloads charges just in selected modules. Action can be " "reversed by holding Ctrl or Alt key while changing charge." @@ -3971,6 +4056,26 @@ msgstr "" msgid "[T2] Void" msgstr "" +#: eos/utils/stats.py:123 +msgid "em" +msgstr "" + +#: eos/utils/stats.py:123 +msgid "exp" +msgstr "" + +#: eos/utils/stats.py:123 +msgid "explosive" +msgstr "" + +#: eos/utils/stats.py:123 +msgid "kin" +msgstr "" + +#: eos/utils/stats.py:123 +msgid "kinetic" +msgstr "" + #: gui/builtinPreferenceViews/pyfaDatabasePreferences.py:38 msgid "pyfa User Path:" msgstr "" @@ -4004,6 +4109,18 @@ msgstr "" msgid "pyfa.io" msgstr "" +#: gui/builtinPreferenceViews/pyfaGeneralPreferences.py:102 +msgid "pyfa:" +msgstr "" + +#: eos/utils/stats.py:123 +msgid "th" +msgstr "" + +#: eos/utils/stats.py:123 +msgid "thermal" +msgstr "" + #: gui/builtinContextMenus/itemMarketJump.py:44 #, python-brace-format msgid "{0} Market Group" @@ -4038,7 +4155,9 @@ msgstr "" #: gui/builtinViewColumns/baseName.py:104 msgid "{} {} Slot" -msgstr "" +msgid_plural "{} {} Slots" +msgstr[0] "" +msgstr[1] "" #: gui/builtinStatsViews/targetingMiscViewMinimal.py:220 #, no-python-format, python-brace-format