Repository: hikari-py/hikari
Pull request #1141 (2022-04-22 19:40:10)

Application Command Localizations
### Summary
This PR is an implementation of [Application Command Localization](https://discord.com/developers/docs/interactions/application-commands#localization)

### Checklist
<!-- Make sure to tick all the following boxes by putting an `x` in between (like this `[x]`) -->
- [X] I have run `nox` and all the pipelines have passed.
- [x] I have made unittests according to the code I have added/modified/deleted.


davfsa: >  (unless @davfsa is ok with merging a public beta but they did a public beta for threads and then made breaking changes 3 months down the line so public beta for discord doesn't guarantee that they won't break stuff).

I would like to have things stable before adding them to the lib. Will check and decide based on that
davfsa: > I would like to have things stable before adding them to the lib. Will check and decide based on that

The idea behind `with_localizations` is to not waste resources if you dont want the whole information (ie, just discovering what commands are available). I would maybe even add it as an argument to disable if people want it to

ref https://github.com/discord/discord-api-docs/pull/4653#discussion_r830352781
MakufonSkifto: > I would maybe even add it as an argument to disable if people want it to

Thats what I originally did. I changed it to always True after a change request.
PerchunPak: As I understand, for finish this PR, someone just need use black on it, and fix docstring?
GoogleGenius: > As I understand, for finish this PR, someone just need use black on it, and fix docstring?

It needs to at least be approved by the maintainers of hikari too.

Patch:
diff --git a/changes/1141.feature.md b/changes/1141.feature.md
new file mode 100644
index 00000000..523d756f
--- /dev/null
+++ b/changes/1141.feature.md
@@ -0,0 +1,1 @@
+Support for Application Command Localizations.
diff --git a/changes/1259.bugfix.md b/changes/1259.bugfix.md
new file mode 100644
index 00000000..343524de
--- /dev/null
+++ b/changes/1259.bugfix.md
@@ -0,0 +1,1 @@
+Fix `reposition_channels` to use the correct route.
diff --git a/dev-requirements/build.txt b/dev-requirements/build.txt
index 47812914..df9bce98 100644
--- a/dev-requirements/build.txt
+++ b/dev-requirements/build.txt
@@ -1,2 +1,2 @@
-setuptools==65.0.2
+setuptools~=65.1.0
 wheel==0.37.1
diff --git a/hikari/api/rest.py b/hikari/api/rest.py
index 5046ffef..1221dd8f 100644
--- a/hikari/api/rest.py
+++ b/hikari/api/rest.py
@@ -6656,7 +6656,7 @@ def command_builder(self, name: str, description: str) -> special_endpoints.Slas
         Parameters
         ----------
         name : builtins.str
-            The command's name. This should match the regex `^[\w-]{1,32}$` in
+            The command's name. This should match the regex `^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$` in
             Unicode mode and be lowercase.
         description : builtins.str
             The description to set for the command.
@@ -6679,7 +6679,7 @@ def slash_command_builder(
         Parameters
         ----------
         name : builtins.str
-            The command's name. This should match the regex `^[\w-]{1,32}$` in
+            The command's name. This should match the regex `^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$` in
             Unicode mode and be lowercase.
         description : builtins.str
             The description to set for the command if this is a slash command.
@@ -6823,6 +6823,12 @@ async def create_slash_command(
         *,
         guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
         options: undefined.UndefinedOr[typing.Sequence[commands.CommandOption]] = undefined.UNDEFINED,
+        name_localizations: undefined.UndefinedOr[
+            typing.Mapping[typing.Union[locales.Locale, str], str]
+        ] = undefined.UNDEFINED,
+        description_localizations: undefined.UndefinedOr[
+            typing.Mapping[typing.Union[locales.Locale, str], str]
+        ] = undefined.UNDEFINED,
         default_member_permissions: typing.Union[
             undefined.UndefinedType, int, permissions_.Permissions
         ] = undefined.UNDEFINED,
@@ -6835,7 +6841,7 @@ async def create_slash_command(
         application: hikari.snowflakes.SnowflakeishOr[hikari.guilds.PartialApplication]
             Object or ID of the application to create a command for.
         name : builtins.str
-            The command's name. This should match the regex `^[\w-]{1,32}$` in
+            The command's name. This should match the regex `^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$` in
             Unicode mode and be lowercase.
         description : builtins.str
             The description to set for the command.
@@ -6849,6 +6855,10 @@ async def create_slash_command(
             a global command rather than a guild specific one.
         options : hikari.undefined.UndefinedOr[typing.Sequence[hikari.commands.CommandOption]]
             A sequence of up to 10 options for this command.
+        name_localizations : hikari.undefined.UndefinedOr[typing.Mapping[typing.Union[hikari.locales.Locale, str], str]]
+            The name localizations for this command.
+        description_localizations : hikari.undefined.UndefinedOr[typing.Mapping[typing.Union[hikari.locales.Locale, str], str]]
+            The name localizations for this command.
         default_member_permissions : typing.Union[hikari.undefined.UndefinedType, int, hikari.permissions.Permissions]
             Member permissions necessary to utilize this command by default.
 
@@ -6897,6 +6907,9 @@ async def create_context_menu_command(
         name: str,
         *,
         guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
+        name_localizations: undefined.UndefinedOr[
+            typing.Mapping[typing.Union[locales.Locale, str], str]
+        ] = undefined.UNDEFINED,
         default_member_permissions: typing.Union[
             undefined.UndefinedType, int, permissions_.Permissions
         ] = undefined.UNDEFINED,
@@ -6913,7 +6926,7 @@ async def create_context_menu_command(
 
             Only USER and MESSAGE are valid here.
         name : builtins.str
-            The command's name. This should match the regex `^[\w-]{1,32}$` in
+            The command's name. This should match the regex `^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$` in
             Unicode mode and be lowercase.
 
         Other Parameters
@@ -6922,6 +6935,8 @@ async def create_context_menu_command(
             Object or ID of the specific guild this should be made for.
             If left as `hikari.undefined.UNDEFINED` then this call will create
             a global command rather than a guild specific one.
+        name_localizations : hikari.undefined.UndefinedOr[typing.Mapping[typing.Union[hikari.locales.Locale, str], str]]
+            The name localizations for this command.
         default_member_permissions : typing.Union[hikari.undefined.UndefinedType, int, hikari.permissions.Permissions]
             Member permissions necessary to utilize this command by default.
 
diff --git a/hikari/api/special_endpoints.py b/hikari/api/special_endpoints.py
index 51aec7a1..b6104ba5 100644
--- a/hikari/api/special_endpoints.py
+++ b/hikari/api/special_endpoints.py
@@ -57,6 +57,7 @@
     from hikari import emojis
     from hikari import files
     from hikari import guilds
+    from hikari import locales
     from hikari import messages
     from hikari import permissions as permissions_
     from hikari import snowflakes
@@ -944,7 +945,7 @@ def name(self) -> str:
         r"""Name to set for this command.
 
         !!! warning
-            This should match the regex `^[\w-]{1,32}$` in Unicode mode
+            This should match the regex `^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$` in Unicode mode
             and must be lowercase.
 
         Returns
@@ -992,6 +993,11 @@ def is_dm_enabled(self) -> undefined.UndefinedOr[bool]:
         Only applicable to globally-scoped commands.
         """
 
+    @property
+    @abc.abstractmethod
+    def name_localizations(self) -> typing.Mapping[typing.Union[locales.Locale, str], str]:
+        """Name localizations set for this command."""
+
     @abc.abstractmethod
     def set_id(self: _T, id_: undefined.UndefinedOr[snowflakes.Snowflakeish], /) -> _T:
         """Set the ID of this command.
@@ -1024,7 +1030,7 @@ def set_default_member_permissions(
         Returns
         -------
         CommandBuilder
-            Object of this command builder for chained calls.
+            Object of this command builder.
         """
 
     @abc.abstractmethod
@@ -1042,6 +1048,23 @@ def set_is_dm_enabled(self: _T, state: undefined.UndefinedOr[bool], /) -> _T:
             Object of this command builder for chained calls.
         """
 
+    @abc.abstractmethod
+    def set_name_localizations(
+        self: _T, name_localizations: typing.Mapping[typing.Union[locales.Locale, str], str], /
+    ) -> _T:
+        """Set the name localizations for this command.
+
+        Parameters
+        ----------
+        name_localizations : hikari.undefined.UndefinedOr[typing.Mapping[typing.Union[hikari.locales.Locale, str], str]]
+            The name localizations to set for this command.
+
+        Returns
+        -------
+        CommandBuilder
+            Object of this command builder.
+        """
+
     @abc.abstractmethod
     def build(self, entity_factory: entity_factory_.EntityFactory, /) -> typing.MutableMapping[str, typing.Any]:
         """Build a JSON object from this builder.
@@ -1108,6 +1131,32 @@ def description(self) -> str:
             The description to set for this command.
         """
 
+    @property
+    @abc.abstractmethod
+    def description_localizations(
+        self,
+    ) -> typing.Mapping[typing.Union[locales.Locale, str], str]:
+        """Command's localised descriptions."""
+
+    @abc.abstractmethod
+    def set_description_localizations(
+        self: _T,
+        description_localizations: typing.Mapping[typing.Union[locales.Locale, str], str],
+        /,
+    ) -> _T:
+        """Set the localised descriptions for this command.
+
+        Parameters
+        ----------
+        description_localizations : hikari.undefined.UndefinedOr[typing.Mapping[typing.Union[hikari.locales.Locale, str], str]]
+            The description localizations to set for this command.
+
+        Returns
+        -------
+        CommandBuilder
+            Object of this command builder.
+        """
+
     @property
     @abc.abstractmethod
     def options(self) -> typing.Sequence[commands.CommandOption]:
diff --git a/hikari/commands.py b/hikari/commands.py
index c58fac18..2909d6f2 100644
--- a/hikari/commands.py
+++ b/hikari/commands.py
@@ -50,6 +50,7 @@
 if typing.TYPE_CHECKING:
     from hikari import channels
     from hikari import guilds
+    from hikari import locales
 
 
 class CommandType(int, enums.Enum):
@@ -133,7 +134,7 @@ class CommandOption:
     r"""The command option's name.
 
     !!! note
-        This will match the regex `^[\w-]{1,32}$` in Unicode mode and will be
+        This will match the regex `^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$` in Unicode mode and will be
         lowercase.
     """
 
@@ -183,6 +184,12 @@ class CommandOption:
     and `builtins.float` if the type is `hikari.commands.OptionType.FLOAT`.
     """
 
+    name_localizations: typing.Mapping[typing.Union[locales.Locale, str], str] = attr.field(factory=dict)
+    """A set of name localizations for this option."""
+
+    description_localizations: typing.Mapping[typing.Union[locales.Locale, str], str] = attr.field(factory=dict)
+    """A set of description localizations for this option"""
+
     min_length: typing.Optional[int] = attr.field(default=None, repr=False)
     """The minimum length permitted (inclusive).
 
@@ -217,7 +224,7 @@ class PartialCommand(snowflakes.Unique):
     r"""The command's name.
 
     !!! note
-        This will match the regex `^[\w-]{1,32}$` in Unicode mode and will be
+        This will match the regex `^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$` in Unicode mode and will be
         lowercase.
     """
 
@@ -239,6 +246,12 @@ class PartialCommand(snowflakes.Unique):
     version: snowflakes.Snowflake = attr.field(eq=False, hash=False, repr=True)
     """Auto-incrementing version identifier updated during substantial record changes."""
 
+    name_localizations: typing.Mapping[typing.Union[locales.Locale, str], str] = attr.field(factory=dict)
+    """A set of name localizations for this command"""
+
+    description_localizations: typing.Mapping[typing.Union[locales.Locale, str], str] = attr.field(factory=dict)
+    """A set of description localizations for this command"""
+
     async def fetch_self(self) -> PartialCommand:
         """Fetch an up-to-date version of this command object.
 
diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py
index 3033ff59..2bbec593 100644
--- a/hikari/impl/entity_factory.py
+++ b/hikari/impl/entity_factory.py
@@ -1771,6 +1771,20 @@ def _deserialize_command_option(self, payload: data_binding.JSONObject) -> comma
         if raw_channel_types := payload.get("channel_types"):
             channel_types = [channel_models.ChannelType(channel_type) for channel_type in raw_channel_types]
 
+        name_localizations: typing.Mapping[str, str]
+        if raw_name_localizations := payload.get("name_localizations"):
+            name_localizations = {locales.Locale(k): raw_name_localizations[k] for k in raw_name_localizations}
+        else:
+            name_localizations = {}
+
+        description_localizations: typing.Mapping[str, str]
+        if raw_description_localizations := payload.get("description_localizations"):
+            description_localizations = {
+                locales.Locale(k): raw_description_localizations[k] for k in raw_description_localizations
+            }
+        else:
+            description_localizations = {}
+
         return commands.CommandOption(
             type=commands.OptionType(payload["type"]),
             name=payload["name"],
@@ -1782,6 +1796,8 @@ def _deserialize_command_option(self, payload: data_binding.JSONObject) -> comma
             autocomplete=payload.get("autocomplete", False),
             min_value=payload.get("min_value"),
             max_value=payload.get("max_value"),
+            name_localizations=name_localizations,
+            description_localizations=description_localizations,
             min_length=payload.get("min_length"),
             max_length=payload.get("max_length"),
         )
@@ -1800,6 +1816,20 @@ def deserialize_slash_command(
         if raw_options := payload.get("options"):
             options = [self._deserialize_command_option(option) for option in raw_options]
 
+        name_localizations: typing.Mapping[typing.Union[locales.Locale, str], str]
+        if raw_name_localizations := payload.get("name_localizations"):
+            name_localizations = {locales.Locale(k): raw_name_localizations[k] for k in raw_name_localizations}
+        else:
+            name_localizations = {}
+
+        description_localizations: typing.Mapping[typing.Union[locales.Locale, str], str]
+        if raw_description_localizations := payload.get("description_localizations"):
+            description_localizations = {
+                locales.Locale(k): raw_description_localizations[k] for k in raw_description_localizations
+            }
+        else:
+            description_localizations = {}
+
         # Discord considers 0 the same thing as ADMINISTRATORS, but we make it nicer to work with
         # by setting it correctly.
         default_member_permissions = payload["default_member_permissions"]
@@ -1820,6 +1850,8 @@ def deserialize_slash_command(
             is_dm_enabled=payload.get("dm_permission", True),
             guild_id=guild_id,
             version=snowflakes.Snowflake(payload["version"]),
+            name_localizations=name_localizations,
+            description_localizations=description_localizations,
         )
 
     def deserialize_context_menu_command(
@@ -1832,6 +1864,12 @@ def deserialize_context_menu_command(
             raw_guild_id = payload["guild_id"]
             guild_id = snowflakes.Snowflake(raw_guild_id) if raw_guild_id is not None else None
 
+        name_localizations: typing.Mapping[typing.Union[locales.Locale, str], str]
+        if raw_name_localizations := payload.get("name_localizations"):
+            name_localizations = {locales.Locale(k): raw_name_localizations[k] for k in raw_name_localizations}
+        else:
+            name_localizations = {}
+
         # Discord considers 0 the same thing as ADMINISTRATORS, but we make it nicer to work with
         # by setting it correctly.
         default_member_permissions = payload["default_member_permissions"]
@@ -1850,6 +1888,7 @@ def deserialize_context_menu_command(
             is_dm_enabled=payload.get("dm_permission", True),
             guild_id=guild_id,
             version=snowflakes.Snowflake(payload["version"]),
+            name_localizations=name_localizations,
         )
 
     def deserialize_command(
@@ -2153,6 +2192,8 @@ def serialize_command_option(self, option: commands.CommandOption) -> data_bindi
             "name": option.name,
             "description": option.description,
             "required": option.is_required,
+            "name_localizations": option.name_localizations,
+            "description_localizations": option.description_localizations,
         }
 
         if option.channel_types is not None:
diff --git a/hikari/impl/rest.py b/hikari/impl/rest.py
index 20ff9441..8c08590e 100644
--- a/hikari/impl/rest.py
+++ b/hikari/impl/rest.py
@@ -2585,7 +2585,7 @@ async def reposition_channels(
         guild: snowflakes.SnowflakeishOr[guilds.PartialGuild],
         positions: typing.Mapping[int, snowflakes.SnowflakeishOr[channels_.GuildChannel]],
     ) -> None:
-        route = routes.POST_GUILD_CHANNELS.compile(guild=guild)
+        route = routes.PATCH_GUILD_CHANNELS.compile(guild=guild)
         body = [{"id": str(int(channel)), "position": pos} for pos, channel in positions.items()]
         await self._request(route, json=body)
 
@@ -3182,7 +3182,10 @@ async def fetch_application_commands(
         else:
             route = routes.GET_APPLICATION_GUILD_COMMANDS.compile(application=application, guild=guild)
 
-        response = await self._request(route)
+        query = data_binding.StringMapBuilder()
+        query.put("with_localizations", True)
+
+        response = await self._request(route, query=query)
         assert isinstance(response, list)
         guild_id = snowflakes.Snowflake(guild) if guild is not undefined.UNDEFINED else None
         return [self._entity_factory.deserialize_command(command, guild_id=guild_id) for command in response]
@@ -3196,6 +3199,12 @@ async def _create_application_command(
         *,
         guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
         options: undefined.UndefinedOr[typing.Sequence[commands.CommandOption]] = undefined.UNDEFINED,
+        name_localizations: undefined.UndefinedOr[
+            typing.Mapping[typing.Union[locales.Locale, str], str]
+        ] = undefined.UNDEFINED,
+        description_localizations: undefined.UndefinedOr[
+            typing.Mapping[typing.Union[locales.Locale, str], str]
+        ] = undefined.UNDEFINED,
         default_member_permissions: typing.Union[
             undefined.UndefinedType, int, permissions_.Permissions
         ] = undefined.UNDEFINED,
@@ -3212,6 +3221,9 @@ async def _create_application_command(
         body.put("description", description)
         body.put("type", type)
         body.put_array("options", options, conversion=self._entity_factory.serialize_command_option)
+        body.put("name_localizations", name_localizations)
+        body.put("description_localizations", description_localizations)
+
         # Discord has some funky behaviour around what 0 means. They consider it to be the same as ADMINISTRATOR,
         # but we consider it to be the same as None for developer sanity reasons
         body.put("default_member_permissions", None if default_member_permissions == 0 else default_member_permissions)
@@ -3229,6 +3241,12 @@ async def create_slash_command(
         *,
         guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
         options: undefined.UndefinedOr[typing.Sequence[commands.CommandOption]] = undefined.UNDEFINED,
+        name_localizations: undefined.UndefinedOr[
+            typing.Mapping[typing.Union[locales.Locale, str], str]
+        ] = undefined.UNDEFINED,
+        description_localizations: undefined.UndefinedOr[
+            typing.Mapping[typing.Union[locales.Locale, str], str]
+        ] = undefined.UNDEFINED,
         default_member_permissions: typing.Union[
             undefined.UndefinedType, int, permissions_.Permissions
         ] = undefined.UNDEFINED,
@@ -3241,6 +3259,8 @@ async def create_slash_command(
             description=description,
             guild=guild,
             options=options,
+            name_localizations=name_localizations,
+            description_localizations=description_localizations,
             default_member_permissions=default_member_permissions,
             dm_enabled=dm_enabled,
         )
@@ -3255,6 +3275,9 @@ async def create_context_menu_command(
         name: str,
         *,
         guild: undefined.UndefinedOr[snowflakes.SnowflakeishOr[guilds.PartialGuild]] = undefined.UNDEFINED,
+        name_localizations: undefined.UndefinedOr[
+            typing.Mapping[typing.Union[locales.Locale, str], str]
+        ] = undefined.UNDEFINED,
         default_member_permissions: typing.Union[
             undefined.UndefinedType, int, permissions_.Permissions
         ] = undefined.UNDEFINED,
@@ -3265,6 +3288,7 @@ async def create_context_menu_command(
             type=type,
             name=name,
             guild=guild,
+            name_localizations=name_localizations,
             default_member_permissions=default_member_permissions,
             dm_enabled=dm_enabled,
         )
diff --git a/hikari/impl/special_endpoints.py b/hikari/impl/special_endpoints.py
index cc760d72..b4d0c3cc 100644
--- a/hikari/impl/special_endpoints.py
+++ b/hikari/impl/special_endpoints.py
@@ -52,6 +52,7 @@
 from hikari import errors
 from hikari import files
 from hikari import iterators
+from hikari import locales
 from hikari import messages
 from hikari import snowflakes
 from hikari import undefined
@@ -1096,6 +1097,8 @@ class CommandBuilder(special_endpoints.CommandBuilder):
     )
     _is_dm_enabled: undefined.UndefinedOr[bool] = attr.field(default=undefined.UNDEFINED, kw_only=True)
 
+    _name_localizations: typing.Mapping[typing.Union[locales.Locale, str], str] = attr.field(factory=dict, kw_only=True)
+
     @property
     def id(self) -> undefined.UndefinedOr[snowflakes.Snowflake]:
         return self._id
@@ -1128,18 +1131,31 @@ def set_is_dm_enabled(self: _CommandBuilderT, state: undefined.UndefinedOr[bool]
         self._is_dm_enabled = state
         return self
 
+    @property
+    def name_localizations(self) -> typing.Mapping[typing.Union[locales.Locale, str], str]:
+        return self._name_localizations
+
+    def set_name_localizations(
+        self: _CommandBuilderT,
+        name_localizations: typing.Mapping[typing.Union[locales.Locale, str], str],
+        /,
+    ) -> _CommandBuilderT:
+        self._name_localizations = name_localizations
+        return self
+
     def build(self, _: entity_factory_.EntityFactory, /) -> typing.MutableMapping[str, typing.Any]:
         data = data_binding.JSONObjectBuilder()
         data["name"] = self._name
         data["type"] = self.type
         data.put_snowflake("id", self._id)
+        data.put("name_localizations", self._name_localizations)
+        data.put("dm_permission", self._is_dm_enabled)
 
         # Discord considers 0 the same thing as ADMINISTRATORS, but we make it nicer to work with
         # by using it correctly.
         if self._default_member_permissions != 0:
             data.put("default_member_permissions", self._default_member_permissions)
 
-        data.put("dm_permission", self._is_dm_enabled)
         return data
 
 
@@ -1150,6 +1166,9 @@ class SlashCommandBuilder(CommandBuilder, special_endpoints.SlashCommandBuilder)
 
     _description: str = attr.field()
     _options: typing.List[commands.CommandOption] = attr.field(factory=list, kw_only=True)
+    _description_localizations: typing.Mapping[typing.Union[locales.Locale, str], str] = attr.field(
+        factory=dict, kw_only=True
+    )
 
     @property
     def description(self) -> str:
@@ -1167,6 +1186,20 @@ def add_option(self: _SlashCommandBuilderT, option: commands.CommandOption) -> _
     def options(self) -> typing.Sequence[commands.CommandOption]:
         return self._options.copy()
 
+    @property
+    def description_localizations(
+        self,
+    ) -> typing.Mapping[typing.Union[locales.Locale, str], str]:
+        return self._description_localizations
+
+    def set_description_localizations(
+        self: _SlashCommandBuilderT,
+        description_localizations: typing.Mapping[typing.Union[locales.Locale, str], str],
+        /,
+    ) -> _SlashCommandBuilderT:
+        self._description_localizations = description_localizations
+        return self
+
     def build(self, entity_factory: entity_factory_.EntityFactory, /) -> typing.MutableMapping[str, typing.Any]:
         data = super().build(entity_factory)
         # Under this context we know this'll always be a JSONObjectBuilder but
@@ -1175,6 +1208,7 @@ def build(self, entity_factory: entity_factory_.EntityFactory, /) -> typing.Muta
         assert isinstance(data, data_binding.JSONObjectBuilder)
         data.put("description", self._description)
         data.put_array("options", self._options, conversion=entity_factory.serialize_command_option)
+        data.put("description_localizations", self._description_localizations)
         return data
 
     async def create(
@@ -1191,6 +1225,8 @@ async def create(
             self._description,
             guild=guild,
             options=self._options,
+            name_localizations=self._name_localizations,
+            description_localizations=self._description_localizations,
             default_member_permissions=self._default_member_permissions,
             dm_enabled=self._is_dm_enabled,
         )


Test patch:
diff --git a/tests/hikari/impl/test_entity_factory.py b/tests/hikari/impl/test_entity_factory.py
index f738ffdc..c4d5d2d6 100644
--- a/tests/hikari/impl/test_entity_factory.py
+++ b/tests/hikari/impl/test_entity_factory.py
@@ -3777,6 +3777,8 @@ def test_serialize_command_option(self, entity_factory_impl):
             max_length=69,
             channel_types=[channel_models.ChannelType.GUILD_STAGE, channel_models.ChannelType.GUILD_TEXT, 100],
             choices=[commands.CommandChoice(name="a", value="choice")],
+            name_localizations={locales.Locale.TR: "b"},
+            description_localizations={locales.Locale.TR: "c"},
             options=[
                 commands.CommandOption(
                     type=commands.OptionType.STRING,
@@ -3785,6 +3787,8 @@ def test_serialize_command_option(self, entity_factory_impl):
                     is_required=False,
                     choices=[commands.CommandChoice(name="boo", value="hoo")],
                     options=None,
+                    name_localizations={locales.Locale.TR: "b"},
+                    description_localizations={locales.Locale.TR: "c"},
                 )
             ],
         )
@@ -3803,6 +3807,8 @@ def test_serialize_command_option(self, entity_factory_impl):
             "max_length": 69,
             "autocomplete": True,
             "choices": [{"name": "a", "value": "choice"}],
+            "description_localizations": {"tr": "c"},
+            "name_localizations": {"tr": "b"},
             "options": [
                 {
                     "type": 3,
@@ -3810,6 +3816,8 @@ def test_serialize_command_option(self, entity_factory_impl):
                     "name": "go home",
                     "required": False,
                     "choices": [{"name": "boo", "value": "hoo"}],
+                    "description_localizations": {"tr": "c"},
+                    "name_localizations": {"tr": "b"},
                 }
             ],
         }
diff --git a/tests/hikari/impl/test_rest.py b/tests/hikari/impl/test_rest.py
index 83abb268..47febacb 100644
--- a/tests/hikari/impl/test_rest.py
+++ b/tests/hikari/impl/test_rest.py
@@ -39,6 +39,7 @@
 from hikari import files
 from hikari import guilds
 from hikari import invites
+from hikari import locales
 from hikari import permissions
 from hikari import scheduled_events
 from hikari import snowflakes
@@ -4031,7 +4032,7 @@ async def test__create_guild_channel(self, rest_client):
         )
 
     async def test_reposition_channels(self, rest_client):
-        expected_route = routes.POST_GUILD_CHANNELS.compile(guild=123)
+        expected_route = routes.PATCH_GUILD_CHANNELS.compile(guild=123)
         expected_json = [{"id": "456", "position": 1}, {"id": "789", "position": 2}]
         rest_client._request = mock.AsyncMock()
 
@@ -4761,7 +4762,7 @@ async def test_fetch_application_commands_with_guild(self, rest_client):
         result = await rest_client.fetch_application_commands(StubModel(54123), StubModel(7623423))
 
         assert result == [rest_client._entity_factory.deserialize_command.return_value]
-        rest_client._request.assert_awaited_once_with(expected_route)
+        rest_client._request.assert_awaited_once_with(expected_route, query={"with_localizations": "true"})
         rest_client._entity_factory.deserialize_command.assert_called_once_with({"id": "34512312"}, guild_id=7623423)
 
     async def test_fetch_application_commands_without_guild(self, rest_client):
@@ -4771,7 +4772,7 @@ async def test_fetch_application_commands_without_guild(self, rest_client):
         result = await rest_client.fetch_application_commands(StubModel(54123))
 
         assert result == [rest_client._entity_factory.deserialize_command.return_value]
-        rest_client._request.assert_awaited_once_with(expected_route)
+        rest_client._request.assert_awaited_once_with(expected_route, query={"with_localizations": "true"})
         rest_client._entity_factory.deserialize_command.assert_called_once_with({"id": "34512312"}, guild_id=None)
 
     async def test__create_application_command_with_optionals(self, rest_client: rest.RESTClientImpl):
@@ -4859,6 +4860,8 @@ async def test_create_slash_command(self, rest_client: rest.RESTClientImpl):
             "not ok anymore",
             guild=mock_guild,
             options=mock_options,
+            name_localizations={locales.Locale.TR: "hhh"},
+            description_localizations={locales.Locale.TR: "jello"},
             default_member_permissions=permissions.Permissions.ADMINISTRATOR,
             dm_enabled=False,
         )
@@ -4874,6 +4877,8 @@ async def test_create_slash_command(self, rest_client: rest.RESTClientImpl):
             description="not ok anymore",
             guild=mock_guild,
             options=mock_options,
+            name_localizations={"tr": "hhh"},
+            description_localizations={"tr": "jello"},
             default_member_permissions=permissions.Permissions.ADMINISTRATOR,
             dm_enabled=False,
         )
@@ -4890,6 +4895,7 @@ async def test_create_context_menu_command(self, rest_client: rest.RESTClientImp
             guild=mock_guild,
             default_member_permissions=permissions.Permissions.ADMINISTRATOR,
             dm_enabled=False,
+            name_localizations={locales.Locale.TR: "hhh"},
         )
 
         assert result is rest_client._entity_factory.deserialize_context_menu_command.return_value
@@ -4903,6 +4909,7 @@ async def test_create_context_menu_command(self, rest_client: rest.RESTClientImp
             guild=mock_guild,
             default_member_permissions=permissions.Permissions.ADMINISTRATOR,
             dm_enabled=False,
+            name_localizations={"tr": "hhh"},
         )
 
     async def test_set_application_commands_with_guild(self, rest_client):
diff --git a/tests/hikari/impl/test_special_endpoints.py b/tests/hikari/impl/test_special_endpoints.py
index aecbf662..0d98004f 100644
--- a/tests/hikari/impl/test_special_endpoints.py
+++ b/tests/hikari/impl/test_special_endpoints.py
@@ -26,6 +26,7 @@
 from hikari import commands
 from hikari import emojis
 from hikari import files
+from hikari import locales
 from hikari import messages
 from hikari import permissions
 from hikari import snowflakes
@@ -674,7 +675,12 @@ def test_build_with_optional_data(self):
         mock_entity_factory = mock.Mock()
         mock_option = object()
         builder = (
-            special_endpoints.SlashCommandBuilder("we are number", "one")
+            special_endpoints.SlashCommandBuilder(
+                "we are number",
+                "one",
+                name_localizations={locales.Locale.TR: "merhaba"},
+                description_localizations={locales.Locale.TR: "bir"},
+            )
             .add_option(mock_option)
             .set_id(3412312)
             .set_default_member_permissions(permissions.Permissions.ADMINISTRATOR)
@@ -692,6 +698,8 @@ def test_build_with_optional_data(self):
             "default_member_permissions": 8,
             "options": [mock_entity_factory.serialize_command_option.return_value],
             "id": "3412312",
+            "name_localizations": {locales.Locale.TR: "merhaba"},
+            "description_localizations": {locales.Locale.TR: "bir"},
         }
 
     def test_build_without_optional_data(self):
@@ -699,13 +707,23 @@ def test_build_without_optional_data(self):
 
         result = builder.build(mock.Mock())
 
-        assert result == {"type": 1, "name": "we are numberr", "description": "oner", "options": []}
+        assert result == {
+            "type": 1,
+            "name": "we are numberr",
+            "description": "oner",
+            "options": [],
+            "name_localizations": {},
+            "description_localizations": {},
+        }
 
     @pytest.mark.asyncio()
     async def test_create(self):
         builder = (
             special_endpoints.SlashCommandBuilder("we are number", "one")
             .add_option(mock.Mock())
+            .set_id(3412312)
+            .set_name_localizations({locales.Locale.TR: "sayı"})
+            .set_description_localizations({locales.Locale.TR: "bir"})
             .set_default_member_permissions(permissions.Permissions.BAN_MEMBERS)
             .set_is_dm_enabled(True)
         )
@@ -720,6 +738,8 @@ async def test_create(self):
             builder.description,
             guild=undefined.UNDEFINED,
             options=builder.options,
+            name_localizations={locales.Locale.TR: "sayı"},
+            description_localizations={locales.Locale.TR: "bir"},
             default_member_permissions=permissions.Permissions.BAN_MEMBERS,
             dm_enabled=True,
         )
@@ -733,6 +753,9 @@ async def test_create_with_guild(self):
         )
         mock_rest = mock.AsyncMock()
 
+        builder.set_name_localizations({locales.Locale.TR: "sayı"})
+        builder.set_description_localizations({locales.Locale.TR: "bir"})
+
         result = await builder.create(mock_rest, 54455445, guild=54123123321)
 
         assert result is mock_rest.create_slash_command.return_value
@@ -742,6 +765,8 @@ async def test_create_with_guild(self):
             builder.description,
             guild=54123123321,
             options=builder.options,
+            name_localizations={locales.Locale.TR: "sayı"},
+            description_localizations={locales.Locale.TR: "bir"},
             default_member_permissions=permissions.Permissions.BAN_MEMBERS,
             dm_enabled=True,
         )
@@ -750,8 +775,12 @@ async def test_create_with_guild(self):
 class TestContextMenuBuilder:
     def test_build_with_optional_data(self):
         builder = (
-            special_endpoints.ContextMenuCommandBuilder(commands.CommandType.USER, "we are number")
+            special_endpoints.ContextMenuCommandBuilder(
+                commands.CommandType.USER,
+                "we are number",
+            )
             .set_id(3412312)
+            .set_name_localizations({locales.Locale.TR: "merhaba"})
             .set_default_member_permissions(permissions.Permissions.ADMINISTRATOR)
             .set_is_dm_enabled(True)
         )
@@ -764,6 +793,7 @@ def test_build_with_optional_data(self):
             "dm_permission": True,
             "default_member_permissions": 8,
             "id": "3412312",
+            "name_localizations": {locales.Locale.TR: "merhaba"},
         }
 
     def test_build_without_optional_data(self):
@@ -771,7 +801,7 @@ def test_build_without_optional_data(self):
 
         result = builder.build(mock.Mock())
 
-        assert result == {"type": 3, "name": "nameeeee"}
+        assert result == {"type": 3, "name": "nameeeee", "name_localizations": {}}
 
     @pytest.mark.asyncio()
     async def test_create(self):