content: [build-system]
  build-backend = "setuptools.build_meta"
  requires = [
    "setuptools>=78.0.2"
  ]
  
  [dependency-groups]
  dev = [
    {include-group = "docs"},
    {include-group = "lint"},
    {include-group = "test"},
    {include-group = "types"}
  ]
  docs = [
    # Do not attempt to build doc on older Python
    "furo==2025.7.19; python_version >= '3.11'",
    "matplotlib==3.10.5; python_version >= '3.11'",
    "sphinx-copybutton==0.5.2; python_version >= '3.11'",
    "Sphinx==8.2.3; python_version >= '3.11'",
    "sphinxext-opengraph==0.12.0; python_version >= '3.11'"
  ]
  lint = [
    {include-group = "pre-commit"}
  ]
  pre-commit = [
    "pre-commit==4.3.0"
  ]
  test = [
    "pytest-cov==6.2.1",
    "pytest-xdist==3.8.0",
    "pytest==8.4.1",
    "syrupy==4.9.1"
  ]
  types = [
    "setuptools>=61.2",
    "types-lxml==2025.3.30",
    "wcwidth-stubs==0.2.14.0"
  ]
  
  [project]
  authors = [
    {email = "translate-devel@lists.sourceforge.net", name = "Translate"}
  ]
  classifiers = [
    "Development Status :: 5 - Production/Stable",
    "Environment :: Console",
    "Intended Audience :: Developers",
    "Operating System :: MacOS :: MacOS X",
    "Operating System :: Microsoft :: Windows",
    "Operating System :: OS Independent",
    "Operating System :: Unix",
    "Programming Language :: Python :: 3 :: Only",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Programming Language :: Python :: 3.13",
    "Programming Language :: Python :: 3.14",
    "Programming Language :: Python :: 3.9",
    "Programming Language :: Python",
    "Topic :: Software Development :: Internationalization",
    "Topic :: Software Development :: Libraries :: Python Modules",
    "Topic :: Software Development :: Localization"
  ]
  dependencies = [
    "lxml>=5.2.0,<6.1",
    "wcwidth>=0.2.14,<0.3"
  ]
  description = "Tools and API for translation and localization engineering."
  dynamic = [
    "version"
  ]
  keywords = [
    "i18n l10n gettext translate"
  ]
  license = "GPL-2.0-or-later"
  license-files = ["COPYING"]
  name = "translate-toolkit"
  requires-python = ">=3.9"
  
  [project.optional-dependencies]
  all = [
    "translate-toolkit[chardet,fluent,ical,ini,levenshtein,markdown,php,rc,spellcheck,subtitles,tmserver,trados,yaml]"
  ]
  chardet = [
    "charset-normalizer>=3.4.0,<4.0"
  ]
  fluent = [
    "fluent.syntax>=0.19.0,<0.20"
  ]
  ical = [
    "vobject==0.9.9"
  ]
  ini = [
    "iniparse==0.5"
  ]
  levenshtein = [
    "python-Levenshtein>=0.21.0"
  ]
  markdown = [
    "mistletoe>=1.4.0,<1.5.0"
  ]
  php = [
    "phply>=1.2.6,<1.3"
  ]
  rc = [
    "pyparsing>=3.2.0,<3.3"
  ]
  spellcheck = [
    "pyenchant==3.3.0"
  ]
  subtitles = [
    "aeidon>=1.14.1,<1.16"
  ]
  tmserver = [
    "cheroot==10.0.1"
  ]
  trados = [
    "BeautifulSoup4>=4.13.0"
  ]
  yaml = [
    "ruamel.yaml>=0.18.0,<0.19.0"
  ]
  
  [project.readme]
  content-type = "text/x-rst"
  file = "README.rst"
  
  [project.scripts]
  android2po = "translate.convert.android2po:main"
  build_tmdb = "translate.tools.build_tmdb:main"
  csv2po = "translate.convert.csv2po:main"
  csv2tbx = "translate.convert.csv2tbx:main"
  dtd2po = "translate.convert.dtd2po:main"
  flatxml2po = "translate.convert.flatxml2po:main"
  html2po = "translate.convert.html2po:main"
  ical2po = "translate.convert.ical2po:main"
  idml2po = "translate.convert.idml2po:main"
  ini2po = "translate.convert.ini2po:main"
  json2po = "translate.convert.json2po:main"
  junitmsgfmt = "translate.tools.junitmsgfmt:main"
  md2po = "translate.convert.md2po:main"
  moz2po = "translate.convert.moz2po:main"
  mozfunny2prop = "translate.convert.mozfunny2prop:main"
  mozlang2po = "translate.convert.mozlang2po:main"
  odf2xliff = "translate.convert.odf2xliff:main"
  oo2po = "translate.convert.oo2po:main"
  oo2xliff = "translate.convert.oo2xliff:main"
  php2po = "translate.convert.php2po:main"
  phppo2pypo = "translate.tools.phppo2pypo:main"
  po2csv = "translate.convert.po2csv:main"
  po2dtd = "translate.convert.po2dtd:main"
  po2flatxml = "translate.convert.po2flatxml:main"
  po2html = "translate.convert.po2html:main"
  po2ical = "translate.convert.po2ical:main"
  po2idml = "translate.convert.po2idml:main"
  po2ini = "translate.convert.po2ini:main"
  po2json = "translate.convert.po2json:main"
  po2md = "translate.convert.po2md:main"
  po2moz = "translate.convert.po2moz:main"
  po2mozlang = "translate.convert.po2mozlang:main"
  po2oo = "translate.convert.po2oo:main"
  po2php = "translate.convert.po2php:main"
  po2prop = "translate.convert.po2prop:main"
  po2rc = "translate.convert.po2rc:main"
  po2resx = "translate.convert.po2resx:main"
  po2sub = "translate.convert.po2sub:main"
  po2symb = "translate.convert.po2symb:main"
  po2tiki = "translate.convert.po2tiki:main"
  po2tmx = "translate.convert.po2tmx:main"
  po2ts = "translate.convert.po2ts:main"
  po2txt = "translate.convert.po2txt:main"
  po2web2py = "translate.convert.po2web2py:main"
  po2wordfast = "translate.convert.po2wordfast:main"
  po2xliff = "translate.convert.po2xliff:main"
  po2yaml = "translate.convert.po2yaml:main"
  poclean = "translate.tools.poclean:main"
  pocompile = "translate.tools.pocompile:main"
  poconflicts = "translate.tools.poconflicts:main"
  pocount = "translate.tools.pocount:main"
  podebug = "translate.tools.podebug:main"
  pofilter = "translate.filters.pofilter:main"
  pogrep = "translate.tools.pogrep:main"
  pomerge = "translate.tools.pomerge:main"
  porestructure = "translate.tools.porestructure:main"
  posegment = "translate.tools.posegment:main"
  poswap = "translate.tools.poswap:main"
  pot2po = "translate.convert.pot2po:main"
  poterminology = "translate.tools.poterminology:main"
  pretranslate = "translate.tools.pretranslate:main"
  prop2mozfunny = "translate.convert.prop2mozfunny:main"
  prop2po = "translate.convert.prop2po:main"
  pydiff = "translate.tools.pydiff:main"
  pypo2phppo = "translate.tools.pypo2phppo:main"
  rc2po = "translate.convert.rc2po:main"
  resx2po = "translate.convert.resx2po:main"
  sub2po = "translate.convert.sub2po:main"
  symb2po = "translate.convert.symb2po:main"
  tbx2po = "translate.convert.tbx2po:main"
  tiki2po = "translate.convert.tiki2po:main"
  tmserver = "translate.services.tmserver:main"
  ts2po = "translate.convert.ts2po:main"
  txt2po = "translate.convert.txt2po:main"
  web2py2po = "translate.convert.web2py2po:main"
  xliff2odf = "translate.convert.xliff2odf:main"
  xliff2oo = "translate.convert.xliff2oo:main"
  xliff2po = "translate.convert.xliff2po:main"
  yaml2po = "translate.convert.yaml2po:main"
  
  [project.urls]
  Documentation = "https://docs.translatehouse.org/projects/translate-toolkit/"
  Download = "https://github.com/translate/translate/releases/"
  Homepage = "https://toolkit.translatehouse.org/"
  "Issue Tracker" = "https://github.com/translate/translate/issues"
  "Source Code" = "https://github.com/translate/translate"
  
  [tool.check-manifest]
  ignore = [
    "*.toml",
    "*.yaml",
    "*.yml",
    ".well-known/funding-manifest-urls",
    "CREDITS",
    "docs/*",
    "docs/*/*",
    "docs/*/*/*",
    "docs/*/*/*/*",
    "Makefile",
    "tests/*",
    "tests/*/*",
    "tests/*/*/*",
    "tests/*/*/*/*",
    "tests/*/*/*/*/*",
    "tools/phase",  # TODO: investigate purpuse, it was never installed
    "tools/phaselist2goals",  # TODO: investigate purpuse, it was never installed
    "translate/convert/odfxml",  # TODO: investigate purpuse, it was never installed
    "translate/convert/roundtrip-gaia",  # TODO: investigate purpuse, it was never installed
    "translate/convert/roundtrip-mozilla",  # TODO: investigate purpuse, it was never installed
    "translate/convert/roundtrip-OOo",  # TODO: investigate purpuse, it was never installed
    "translate/storage/debug.properties"  # TODO: this should be converted to tests
  ]
  
  [tool.isort]
  known_third_party = [
    "bs4",
    "cheroot",
    "iniparse",
    "lxml",
    "phply",
    "pyparsing",
    "pytest",
    "ruamel",
    "setuptools",
    "sphinx",
    "vobject"
  ]
  profile = "black"
  
  [tool.pytest.ini_options]
  norecursedirs = ".git build docs"
  
  [tool.ruff]
  target-version = "py39"
  
  [tool.ruff.lint]
  extend-safe-fixes = [
    "FURB113",
    "PLC2801",
    "TCH003"
  ]
  # CONFIG - intentional configuration
  # TODO - needs decision whether intention, add noqa tags or fix
  # WONTFIX - not fixable in current codebase, might be better to go for noqa
  ignore = [
    "A001",  # TODO: Variable is shadowing a Python builtin
    "A002",  # TODO: overriding builtins (might need noqa tags)
    "A005",  # TODO: Module `statistics` is shadowing a Python builtin module
    "ANN",  # TODO: type annotations missing
    "ARG001",  # TODO: Unused function argument (mostly for API compatibility)
    "ARG002",  # TODO: Unused method argument (mostly for API compatibility)
    "ARG004",  # TODO: Unused static method argument
    "ARG005",  # TODO: Unused lambda argument:
    "B006",  # TODO: Do not use mutable data structures for argument defaults
    "B020",  # TODO: Loop control variable `node` overrides iterable it iterates
    "B028",  # TODO: No explicit `stacklevel` keyword argument found
    "B904",  # TODO: Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling
    "B909",  # X-TODO: Mutation to loop iterable `inputpo.units` during iteration
    "BLE001",  # WONTFIX: Do not catch blind exception: `Exception`, third-party modules do not have defined exceptions
    "C901",  # TODO: too complex
    "COM",  # CONFIG: No trailing commas
    "CPY001",  # TODO: Missing copyright notice at top of file
    "D1",  # TODO: Missing docstring
    "D203",  # CONFIG: incompatible with D211
    "D205",  # TODO: 1 blank line required between summary line and description
    "D206",  # CONFIG: formatter
    "D212",  # CONFIG: incompatible with D213
    "D401",  # TODO: First line of docstring should be in imperative mood
    "D404",  # TODO: First word of the docstring should not be "This"
    "D409",  # TODO: Section underline should match the length of its name
    "DOC",  # TODO: is not documented in docstring
    "DTZ005",  # TODO: The use of `datetime.datetime.now()` without `tz` argument is not allowed
    "E262",  # TODO: Inline comment should start with `# `
    "E265",  # TODO: Block comment should start with `# `
    "E266",  # TODO: Too many leading `#` before block comment
    "E501",  # WONTFIX: we accept long strings (rest is formatted by black)
    "E741",  # TODO: Ambiguous variable name:
    "EM",  # TODO: Exception strings
    "ERA001",  # TODO: Found commented-out code
    "FBT",  # TODO: Boolean in function definition
    "FIX001",  # TODO: Line contains FIXME, consider resolving the issue
    "FIX002",  # CONFIG: we use TODO
    "FIX003",  # TODO: Line contains XXX, consider resolving the issue
    "FIX004",  # TODO: Line contains HACK, consider resolving the issue
    "FURB101",  # TODO: `open` and `read` should be replaced by `Path(file_).read_bytes()`
    "FURB103",  # TODO: `open` and `write` should be replaced by `Path(outputfile.name).write_bytes(outputmo)`
    "FURB189",  # TODO: Subclassing `dict` can be error prone, use `collections.UserDict` instead
    "INP001",  # TODO: File is part of an implicit namespace package. Add an `__init__.py`.
    "N801",  # TODO: Class name should use CapWords convention
    "N802",  # TODO: Function name `FAIL` should be lowercase
    "N803",  # TODO: Argument name `attributeValue` should be lowercase
    "N806",  # TODO: Variable `sourcelanguageNode` in function should be lowercase
    "N813",  # TODO: Camelcase `StateEnum` imported as lowercase `state`
    "N815",  # TODO: Variable `bodyNode` in class scope should not be mixedCase
    "N816",  # TODO: Variable `theDOMImplementation` in global scope should not be mixedCase
    "N818",  # TODO: Exception name `FilterFailure` should be named with an Error suffix
    "PLC0415",  # TODO: `import` should be at the top-level of a file
    "PLE2502",  # TODO: Contains control characters that can permit obfuscated code
    "PLR0904",  # TODO: Too many public methods (21 > 20)
    "PLR0911",  # TODO: Too many return statements
    "PLR0912",  # TODO: Too many branches
    "PLR0913",  # TODO: Too many arguments in function definition
    "PLR0914",  # TODO: Too many local variables (16/15)
    "PLR0915",  # TODO: Too many statements
    "PLR0916",  # TODO: Too many Boolean expressions (6 > 5)
    "PLR0917",  # TODO: Too many positional arguments (7/5)
    "PLR1702",  # TODO: Too many nested blocks (8 > 5)
    "PLR2004",  # TODO: Magic value used in comparison, consider replacing with a constant variable
    "PLR6301",  # TODO: Method `entry` could be a function, class method, or static method
    "PLW1510",  # TODO: `subprocess.run` without explicit `check` argument
    "PLW1514",  # TODO: `open` in text mode without explicit `encoding` argument
    "PLW1641",  # TODO: Object does not implement `__hash__` method
    "PLW2901",  # TODO: `for` loop variable `input_unit` overwritten by assignment target
    "PT009",  # TODO: Use a regular `assert` instead of unittest-style `assertCountEqual`
    "PT011",  # TODO: `pytest.raises(ValueError)` is too broad, set the `match` parameter or use a more specific exception
    "PT013",  # TODO: Found incorrect import of pytest, use simple `import pytest` instead
    "PTH",  # TODO: Not using pathlib for now
    "RUF001",  # WONTFIX: String contains ambiguous unicode character, we are using Unicode
    "RUF002",  # WONTFIX: Docstring contains ambiguous
    "RUF003",  # WONTFIX: Comment contains ambiguous
    "RUF012",  # TODO: Mutable class attributes should be annotated with `typing.ClassVar`
    "S101",  # CONFIG: Use of `assert` detected
    "S301",  # TODO: `pickle` and modules that wrap it can be unsafe when used to deserialize untrusted data, possible security issue
    "S403",  # TODO: `pickle`, `cPickle`, `dill`, and `shelve` modules are possibly insecure
    "S404",  # TODO: `subprocess` module is possibly insecure
    "S407",  # TODO: `xml.dom.expatbuilder` is vulnerable to XML attacks
    "S408",  # TODO: `xml.dom.minidom` is vulnerable to XML attacks
    "S603",  # CONFIG: `subprocess` call: check for execution of untrusted input
    "S607",  # CONFIG: executing system installed tools
    "SIM102",  # TODO: Use a single `if` statement instead of nested `if` statements
    "SIM115",  # TODO: Use context handler for opening files
    "SLF001",  # TODO: Private member accessed
    "SLOT000",  # TODO: Subclasses of `str` should define `__slots__`
    "TD001",  # TODO: Invalid TODO tag: `FIXME`
    "TD002",  # CONFIG: no detailed TODO documentation is required
    "TD003",  # CONFIG: no detailed TODO documentation is required
    "TD004",  # TODO: Missing colon in TODO
    "TD005",  # TODO: Missing issue description after `TODO`
    "TD007",  # TODO: Missing space after colon in TODO
    "TID252",  # TODO: Relative imports from parent modules are banned
    "TRY003",  # WONTFIX: Avoid specifying long messages outside the exception class
    "UP031",  # X-TODO: Use format specifiers instead of percent format
    'ISC001',  # CONFIG: formatter
    'Q000',  # CONFIG: formatter
    'Q001',  # CONFIG: formatter
    'Q002',  # CONFIG: formatter
    'Q003',  # CONFIG: formatter
    'W191'  # CONFIG: formatter
  ]
  preview = true
  select = ["ALL"]
  
  [tool.ruff.lint.per-file-ignores]
  "tests/**.py" = ["PLC1901", "T201"]
  "translate/misc/progressbar.py" = ["T201"]
  "translate/storage/benchmark.py" = ["T201"]
  "translate/storage/placeables/strelem.py" = ["T201"]
  "translate/tools/**.py" = ["T201"]
  
  [tool.setuptools]
  include-package-data = true
  platforms = [
    "any"
  ]
  script-files = [
    "tools/pocommentclean",
    "tools/pocompendium",
    "tools/pomigrate2",
    "tools/popuretext",
    "tools/poreencode",
    "tools/posplit"
  ]
  zip-safe = false
  
  [tool.setuptools.dynamic.version]
  attr = "translate.__version__.sver"
  
  [tool.setuptools.packages.find]
  include = [
    "translate*"
  ]
  namespaces = true
  
  [tool.tomlsort]
  ignore_case = true
  sort_inline_arrays = true
  sort_inline_tables = true
  sort_table_keys = true
  spaces_before_inline_comment = 2
  
  [tool.typos.default]
  extend-ignore-re = [
    "(?Rm)^.*# codespell:ignore.*$",
    "(?s)(#|//)\\s*spellchecker:off.*?\\n\\s*(#|//)\\s*spellchecker:on"
  ]
  
  [tool.typos.default.extend-identifiers]
  plasTeX = "plasTeX"
  
  [tool.typos.default.extend-words]
  # Open Document Format acronym
  ODF = "ODF"
  Uniforum = "Uniforum"
  # Author name
  Wil = "Wil"
  
  [tool.typos.files]
  extend-exclude = [
    "docs/commands/poterminology_stopword_file.rst",
    "tests/odf_xliff/test_inline-test_odf2xliff_inline-reference.xlf",
    "tests/translate/filters/test_checks.py",
    "tests/translate/lang/test_*.py",
    "tests/translate/lang/test_identify.py",
    "tests/xliff_conformance/af-pootle.po",
    "tests/xliff_conformance/en-US.sdf",
    "translate/lang/data.py",
    "translate/lang/st.py",
    "translate/share/langmodels/*.lm",
    "translate/share/stoplist-en"
  ]
  