Repository: WeblateOrg/weblate

Issue:
Posix lowercase is not avaiable for Chinese
### Describe the issue

As #9377 described, Posix lowercase is the format that Minecraft uses.

According to the source code, this function was implemented by lower the code from `EXPAND_LANGS`. But zh_cn, zh_tw, etc are not included in it, they are included in `LEGACY_CODES`, so they are ignored. For example, Chinese Simplified is `zh_hans` instead of `zh_cn`.

### I already tried

- [X] I've read and searched [the documentation](https://docs.weblate.org/).
- [X] I've searched for similar filed issues in this repository.

### Steps to reproduce the behavior

1. Set Language code style as "POSIX style using underscore as a separator, including country code (lowercase)".
2. Add Chinese Simplified or Chinese Traditional for the component.

### Expected behavior

The file name of Chinese Simplified should be `zh_cn`, Chinese Traditional should be `zh_tw`, and so on.

### Screenshots

_No response_

### Exception traceback

_No response_

### How do you run Weblate?

Docker container

### Weblate versions

_No response_

### Weblate deploy checks

_No response_

### Additional context

_No response_


Discussion:
Yes, POSIX uses codes like `zh_Hant`, Linux translates it to `zh_TW`. So, maybe you actually need Linux lowercase?
>So, maybe you actually need Linux lowercase?

I think yeah. There is no style that matches Minecraft.
This issue seems to be a good fit for newbie contributors. You are welcome to contribute to Weblate! Don't hesitate to ask any questions you would have while implementing this.

You can learn about how to get started in our [contributors documentation](https://docs.weblate.org/en/latest/contributing/index.html).

Resolving patch:
diff --git a/weblate/formats/base.py b/weblate/formats/base.py
--- a/weblate/formats/base.py
+++ b/weblate/formats/base.py
@@ -557,6 +557,10 @@ def get_language_code(cls, code: str, language_format: str | None = None) -> str
     def get_language_posix(code: str) -> str:
         return code.replace("-", "_")
 
+    @classmethod
+    def get_language_posix_lowercase(cls, code: str) -> str:
+        return cls.get_language_posix(code).lower()
+
     @staticmethod
     def get_language_bcp(code: str) -> str:
         return code.replace("_", "-")
@@ -578,6 +582,10 @@ def get_language_linux(cls, code: str) -> str:
         """Linux doesn't use Hans/Hant, but rather TW/CN variants."""
         return LEGACY_CODES.get(code, cls.get_language_posix(code))
 
+    @classmethod
+    def get_language_linux_lowercase(cls, code: str) -> str:
+        return cls.get_language_linux(code).lower()
+
     @classmethod
     def get_language_bcp_long(cls, code: str) -> str:
         return cls.get_language_bcp(cls.get_language_posix_long(code))
diff --git a/weblate/trans/migrations/0015_alter_component_language_code_style.py b/weblate/trans/migrations/0015_alter_component_language_code_style.py
new file mode 100644
--- /dev/null
+++ b/weblate/trans/migrations/0015_alter_component_language_code_style.py
@@ -0,0 +1,58 @@
+# Copyright © Michal Čihař <michal@weblate.org>
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Generated by Django 5.0.3 on 2024-04-25 08:53
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ("trans", "0014_alter_component_repoweb_alter_project_web"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="component",
+            name="language_code_style",
+            field=models.CharField(
+                blank=True,
+                choices=[
+                    ("", "Default based on the file format"),
+                    ("posix", "POSIX style using underscore as a separator"),
+                    (
+                        "posix_lowercase",
+                        "POSIX style using underscore as a separator, lower cased",
+                    ),
+                    ("bcp", "BCP style using hyphen as a separator"),
+                    (
+                        "posix_long",
+                        "POSIX style using underscore as a separator, including country code",
+                    ),
+                    (
+                        "posix_long_lowercase",
+                        "POSIX style using underscore as a separator, including country code, lower cased",
+                    ),
+                    (
+                        "bcp_long",
+                        "BCP style using hyphen as a separator, including country code",
+                    ),
+                    (
+                        "bcp_legacy",
+                        "BCP style using hyphen as a separator, legacy language codes",
+                    ),
+                    ("bcp_lower", "BCP style using hyphen as a separator, lower cased"),
+                    ("android", "Android style"),
+                    ("appstore", "Apple App Store metadata style"),
+                    ("googleplay", "Google Play metadata style"),
+                    ("linux", "Linux style"),
+                    ("linux_lowercase", "Linux style, lower cased"),
+                ],
+                default="",
+                help_text="Customize language code used to generate the filename for translations created by Weblate.",
+                max_length=20,
+                verbose_name="Language code style",
+            ),
+        ),
+    ]
diff --git a/weblate/trans/models/component.py b/weblate/trans/models/component.py
--- a/weblate/trans/models/component.py
+++ b/weblate/trans/models/component.py
@@ -125,6 +125,10 @@
 LANGUAGE_CODE_STYLE_CHOICES = (
     ("", gettext_lazy("Default based on the file format")),
     ("posix", gettext_lazy("POSIX style using underscore as a separator")),
+    (
+        "posix_lowercase",
+        gettext_lazy("POSIX style using underscore as a separator, lower cased"),
+    ),
     ("bcp", gettext_lazy("BCP style using hyphen as a separator")),
     (
         "posix_long",
@@ -135,7 +139,7 @@
     (
         "posix_long_lowercase",
         gettext_lazy(
-            "POSIX style using underscore as a separator, including country code (lowercase)"
+            "POSIX style using underscore as a separator, including country code, lower cased"
         ),
     ),
     (
@@ -151,6 +155,7 @@
     ("appstore", gettext_lazy("Apple App Store metadata style")),
     ("googleplay", gettext_lazy("Google Play metadata style")),
     ("linux", gettext_lazy("Linux style")),
+    ("linux_lowercase", gettext_lazy("Linux style, lower cased")),
 )
 
 MERGE_CHOICES = (


Test patch:
diff --git a/weblate/trans/tests/test_newlang.py b/weblate/trans/tests/test_newlang.py
--- a/weblate/trans/tests/test_newlang.py
+++ b/weblate/trans/tests/test_newlang.py
@@ -202,6 +202,10 @@ def perform(style, code, expected) -> None:
         perform("bcp", "pt_BR", "pt-BR")
         perform("bcp_long", "ms", "ms-MY")
         perform("android", "pt_BR", "pt-rBR")
+        perform("linux_lowercase", "pt_BR", "pt_br")
+        perform("linux_lowercase", "zh_Hant", "zh_tw")
+        perform("posix_lowercase", "pt_BR", "pt_br")
+        perform("posix_lowercase", "zh_Hant", "zh_hant")
 
         self.project.language_aliases = "ia_FOO:ia"
         self.project.save()