Repository: ansible-collections/community.general

Issue:
zypper remove ignored which LANG=fr_FR
##### SUMMARY
The `zypper` module does not remove packages with `state: absent` when the local is set something other than 'en_US'.

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
- plugins/modules/packaging/os/zypper.py

##### ANSIBLE VERSION
```paste below
ansible 2.9.14
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/xenlo/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.8/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.8.6 (default, Sep 25 2020, 00:00:00) [GCC 10.2.1 20200723 (Red Hat 10.2.1-1)]
```

##### CONFIGURATION
<!--- Paste verbatim output from "ansible-config dump --only-changed" between quotes -->
```paste below
DEFAULT_STDOUT_CALLBACK(/etc/ansible/ansible.cfg) = yaml
DEFAULT_VAULT_PASSWORD_FILE(env: ANSIBLE_VAULT_PASSWORD_FILE) = /home/xenlo/.ansible/vault_keyring
```

##### OS / ENVIRONMENT
*Controler*:
- Fedora 32
- LANG: fr_BE.UTF-8

*Target host*:
- SLES 15
- LANG: POSIX

##### STEPS TO REPRODUCE

On a target SLES (or openSUSE) host ensure that a rpm package is installed with zypper:
```shell
target:~ # zypper install libwbclient0
[…]
target:~ # zypper search libwbclient0
[…]
S | Nom          | Résumé                    | Type  
--+--------------+---------------------------+-------
i | libwbclient0 | Samba libwbclient Library | paquet
```

And from there try to remove from an Ansible controle with an other LANG than 'en_US' (either with `ansible` ad-hoc or `ansible-playbook):
```yaml
controler:~/Projects/344$ LANG=fr_FR.UTF8  ansible -i inventories/ target -m zypper -a "name=libwbclient0 state=absent" -u root
target | SUCCESS => {​​​​​​​
    "changed": false,
    "name": [
        "libwbclient0"
    ],
    "rc": 0,
    "state": "absent",
    "update_cache": false
}​​​​​​​
```
You get `"changed": false,` and `"state": "absent",`.

##### EXPECTED RESULTS
I expect to have the package removed from the target and as output "changed: true". Just like if I run the same ansible command but with 'en_US'. 

```shell
controler:~/Projects/344$ LANG=en_US.UTF8 ansible -i inventories/ target -m zypper -a "name=colordiff state=absent" -u root
target | CHANGED => {
    "changed": true,
    "cmd": [
        "/usr/bin/zypper",
        "--quiet",
        "--non-interactive",
        "--xmlout",
        "remove",
        "--type",
        "package",
        "colordiff"
    ],
    "name": [
        "colordiff"
    ],
    "rc": 0,
    "state": "absent",
    "stderr": "",
    "stderr_lines": [],
    "stdout": "<?xml version='1.0'?>\n<stream>\n<install-summary download-size=\"0\" space-usage-diff=\"-54086\">\n<to-remove>\n<solvable type=\"package\" name=\"colordiff\" edition=\"1.0.18-bp150.2.4\" arch=\"noarch\" summary=\"Colour-highlighted &apos;diff&apos; output\">\n<description>The Perl script colordiff is a wrapper for &apos;diff&apos; and produces the same\noutput but with pretty &apos;syntax&apos; highlighting. Colour schemes can be\ncustomized.</description></solvable>\n</to-remove>\n</install-summary>\n<prompt id=\"0\">\n<text>Continue?</text>\n<option default=\"1\" value=\"y\" desc=\"Yes, accept the summary and proceed with installation/removal of packages.\"/>\n<option value=\"n\" desc=\"No, cancel the operation.\"/>\n<option value=\"v\" desc=\"Toggle display of package versions.\"/>\n<option value=\"a\" desc=\"Toggle display of package architectures.\"/>\n<option value=\"r\" desc=\"Toggle display of repositories from which the packages will be installed.\"/>\n<option value=\"m\" desc=\"Toggle display of package vendor names.\"/>\n<option value=\"d\" desc=\"Toggle between showing all details and as few details as possible.\"/>\n<option value=\"g\" desc=\"View the summary in pager.\"/>\n</prompt>\n</stream>\n",
    "stdout_lines": [
        "<?xml version='1.0'?>",
        "<stream>",
        "<install-summary download-size=\"0\" space-usage-diff=\"-54086\">",
        "<to-remove>",
        "<solvable type=\"package\" name=\"colordiff\" edition=\"1.0.18-bp150.2.4\" arch=\"noarch\" summary=\"Colour-highlighted &apos;diff&apos; output\">",
        "<description>The Perl script colordiff is a wrapper for &apos;diff&apos; and produces the same",
        "output but with pretty &apos;syntax&apos; highlighting. Colour schemes can be",
        "customized.</description></solvable>",
        "</to-remove>",
        "</install-summary>",
        "<prompt id=\"0\">",
        "<text>Continue?</text>",
        "<option default=\"1\" value=\"y\" desc=\"Yes, accept the summary and proceed with installation/removal of packages.\"/>",
        "<option value=\"n\" desc=\"No, cancel the operation.\"/>",
        "<option value=\"v\" desc=\"Toggle display of package versions.\"/>",
        "<option value=\"a\" desc=\"Toggle display of package architectures.\"/>",
        "<option value=\"r\" desc=\"Toggle display of repositories from which the packages will be installed.\"/>",
        "<option value=\"m\" desc=\"Toggle display of package vendor names.\"/>",
        "<option value=\"d\" desc=\"Toggle between showing all details and as few details as possible.\"/>",
        "<option value=\"g\" desc=\"View the summary in pager.\"/>",
        "</prompt>",
        "</stream>"
    ],
    "update_cache": false
```
##### ACTUAL RESULTS
Output with `-vvvv` in [this gist](https://gist.github.com/xenlo/bd49aa660a32b4df9a64fa94f61e3d71).



Discussion:
Files identified in the description:
* [`plugins/modules/packaging/os/zypper.py`](https://github.com/ansible-collections/community.general/blob/master/plugins/modules/packaging/os/zypper.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @AnderEnder @alxgu @andytom @commel @dcermak @evrardjp @lrupp @toabctl
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
According to my colleague's investigations, the `zypper` module use the xml output of zypper to check the state of a package. It looks for attribute '_name_' when in French it get translated form: '_nom_'.

- **en_US**: `<solvable status="installed" name="libwbclient0" kind="package" edition="4.7.6+git.54.6e3276c9872-2.47" arch="x86_64" repository="PTS-SLES-15_basesystem-x86_64"/>`
- **fr_FR**: `<solvable status="installed" nom="libwbclient0" kind="paquet" edition="4.7.6+git.54.6e3276c9872-2.47" arch="x86_64" dépôt="PTS-SLES-15_basesystem-x86_64"/>`

Which explains the strange behaviour. 

Is it possible to enforce en_US LANG on the module execution on the target host to ensure that the output check catch what it expect to catch?
I'd rather set it to `C`, as the `en_US` locale is not available everywhere. It should be easy to do that, similar to here: https://github.com/ansible-collections/community.general/blob/main/plugins/modules/cloud/atomic/atomic_image.py#L93

In any case, 'translating' XML property names looks like a bug to me. This should probably also be reported as a bug in zypper.
Indeed, I will look on Monday if I can manage to propose PR. And also have look where I can open an issue upstream to escalate the xml proprties translation. 
i can't reproduce on opensuse leap 15.2 (don't have SLES installed) , but probably the following patch fixes the issue? 
```
diff --git a/plugins/modules/packaging/os/zypper.py b/plugins/modules/packaging/os/zypper.py
index 5997cfa4..f6c623ef 100644
--- a/plugins/modules/packaging/os/zypper.py
+++ b/plugins/modules/packaging/os/zypper.py
@@ -515,6 +515,8 @@ def main():
         supports_check_mode=True
     )
 
+    module.run_command_environ_update = dict(LANG='C', LC_ALL='C', LC_MESSAGES='C')
+
     name = module.params['name']
     state = module.params['state']
     update_cache = module.params['update_cache']
```
I just give it a try and was able to reporduce on a OpenSUSE 15.1.

Resolving patch:
diff --git a/plugins/modules/packaging/os/zypper.py b/plugins/modules/packaging/os/zypper.py
--- a/plugins/modules/packaging/os/zypper.py
+++ b/plugins/modules/packaging/os/zypper.py
@@ -515,6 +515,8 @@ def main():
         supports_check_mode=True
     )
 
+    module.run_command_environ_update = dict(LANG='C', LC_ALL='C', LC_MESSAGES='C')
+
     name = module.params['name']
     state = module.params['state']
     update_cache = module.params['update_cache']


Test patch: