mineral-dart / core

🧡 The neuralgic heart of the application, this module gathers all the functionalities of the framework.
https://mineral-foundation.org
MIT License
25 stars 8 forks source link

[Commands] - Implement translations #171

Closed LeadcodeDev closed 1 month ago

LeadcodeDev commented 1 month ago

Context

Translations are a very important part of orders when they are available on several servers. We assume that a language can only be defined once.

We can therefore consider a parameter called translation which would allow the different translations to be defined via a Set.

// Define in code
final command = SlashCommandBuilder()
  .setName('name', translation: Translation.sets({
    Lang.en("name"),
    Lang.fr("nom"),
    Lang.de("name"),
});

For those wishing to use a different data format, we'd like to keep the approach as close as possible to that explained in the Discord API documentation.

final command = SlashCommandBuilder()
  .setName('name', translation: Translation.json({
     'en': 'name',
     'fr': 'nom',
     'de': 'name',
}));

Our philosophy is to adopt a "code first" declaration approach, while allowing end developers to externalise the verbose part in a yaml or json configuration.

In this way, we can envisage using a class to 'load' the textual content of a file according to its yaml, json or toml extension.

final command = SlashCommandBuilder()
  .setName('name', file: Disk.file(''), uid: 'uid')); 👈 // Auto-select label key
  .setDescription('default', file: Disk.file(''), uid: 'uid')); 👈 // Auto-select description key

  // Or with the shortcut
  .identify(file: Disk.file(''), uid: 'uid')

The data structure in the translation files must follow this nomenclature. The uid parameter is a unique identifier that the developer must provide when loading the file to enable the framework to map the translations correctly.

commands:
  [uid]:
    label:
      - en: Name
      - fr: Nom
    description:
      - en: Name description
      - fr: Description du nom

We can therefore envisage a utility class replacing File within the Mineral context.

abstract interface class DiskContract {
  String get filename;
  String get ext;
  String get raw;
  Map<String, dynamic> get content;
}

final class Disk implements DiskContract {
  factory Disk.yaml() {}
  factory Disk.json() {}
  factory Disk.file() {}
}

Steps

Acceptances

LeadcodeDev commented 1 month ago

Using file as base command.

final commandBuilder = CommandDefinition()
    ..using(File('config/commands/test_commands.yaml'))
    ..setHandler('test.add', (CommandContext _) => print('Add rôle'))
    ..setHandler('test.remove', (CommandContext _) => print('Remove rôle'));
groups:
  [group_identifier]:
    name:
      _default: group1
      [lang]: group1
    description:
      _default: Group 1 description
      [lang]: First group description

commands:
  [command_identifier]:
    name:
      _default: role
     [lang]: role
    description:
      _default: Role manager
      [lang]: Role manager

  [command_identifier].[sub_identifier]:
    name:
      _default: add
      [lang]: add
    description:
      _default: Add role
      [lang]: Add role
    options:
      - type: string
        required: true
        name:
          _default: str
          [lang]: str
        description:
          _default: str description
          [lang]: str description

  [command_identifier].[sub_identifier]:
    name:
      _default: remove
      [lang]: value
    description:
      _default: Remove role
      [lang]: Remove role