Улучшение механизма переводов для бедняков в программе c ++ 98 (продолжение)

В продолжение этого вопроса пытаться
чтобы добавить базовые возможности перевода в старую программу на C ++ 98 ради сообщества, я публикую здесь первую итерацию моего код
после применения некоторых пунктов, вытекающих из предыдущего вопроса:

#include <string>
#include <iostream>

// Not nested in class definition to avoid class namespace in code
enum
   {
    TR_RELOAD=0,
    TR_SAVE,
    TR_SIZ
   };

class PoorMansTranslations
{
 private:
    std::string i_tr[TR_SIZ]; // Actual translation strings

    // Here to be near the enum, consistency is by hand!
    typedef char tr_chars_t[TR_SIZ][16]; // Translation string tables with static linkage
    static const tr_chars_t& resolve_lang(const std::string& lang) throw()
       {
        static const tr_chars_t tr_en =
           {
            "Reload", // TR_RELOAD
            "Save"    // TR_SAVE
           };

        static const tr_chars_t tr_it =
           {
            "Ricarica", // TR_RELOAD
            "Salva"     // TR_SAVE
           };

        static const tr_chars_t tr_es =
           {
            "Recargar", // TR_RELOAD
            "Salvar",   // TR_SAVE
           };

        static const tr_chars_t tr_fr =
           {
            "Recharger",  // TR_RELOAD
            "Enregistrer" // TR_SAVE
           };

             if(lang=="en") return tr_en;
        else if(lang=="it") return tr_it;
        else if(lang=="fr") return tr_fr;
        else if(lang=="es") return tr_es;
        return tr_en; // Fallback lang
       }

 public:
    const std::string& operator[](const std::size_t idx) const throw() { return i_tr[idx]; }

    void select_lang(const std::string& lang) throw()
       {
        const tr_chars_t& tr_arr = resolve_lang(lang);
        // Constructing the strings on demand
        for(int i=0; i<TR_SIZ; ++i) i_tr[i] = std::string(tr_arr[i]);
       }
} tr;


int main()
{
    std::string lang = "it"; // note: unknown at compile time

    tr.select_lang(lang);
    std::cout << tr[TR_RELOAD] << ", " << tr[TR_SAVE] << 'n';

    lang = "bad";
    tr.select_lang(lang);
    std::cout << tr[TR_RELOAD] << ", " << tr[TR_SAVE] << 'n';
}
```

1 ответ
1

Избегайте жесткого кодирования строк перевода до 16 байтов

Нет необходимости устанавливать размер строк равным 16 байтам, просто сохраните их как указатели на const char вместо массивов char:

typedef const char *tr_chars_t[TR_SIZ];

Хранить все языки в массиве

Для каждого языка у вас есть отдельная переменная, содержащая строки переводов, и у вас есть длинная цепочка if-then, чтобы найти нужную переменную с учетом имени языка. Вы можете сделать код более универсальным, сохранив все языки в массиве, например:

static const tr_chars_t &resolve_lang(const std::string &lang) throw()
{
    static const struct {
        const char *lang;
        const tr_chars_t translations;
    } languages[] = {
        {
            "en", {
                "Reload", // TR_RELOAD
                "Save"    // TR_SAVE
            }
        },
        {
            "it", {
                "Ricarica", // TR_RELOAD
                "Salva"     // TR_SAVE
            }
        },
        ...
    };

    static const std::size_t n_languages = sizeof languages / sizeof * languages;

    for (std::size_t i = 0; i < n_languages; ++i) {
        if (lang == languages[i].lang) {
            return languages[i].translations;
        }
    }

    return languages[0].translations; // Fallback lang
}

Таким образом, при добавлении языка вам нужно только добавить запись в массив languages[]и не изменять какой-либо другой код.

Вы даже можете немного улучшить его, убедившись, что языки упорядочены в алфавитном порядке, чтобы вы могли выполнять двоичный поиск.

Рассмотрите возможность использования алгоритмов STL

Копирование строк C в массив std::strings можно сделать с std::copy():

void select_lang(const std::string &lang) throw()
{
    const tr_chars_t &tr_arr = resolve_lang(lang);
    std::copy(tr_arr, tr_arr + TR_SIZ, i_tr);
}

Вы также можете использовать std::find() или std::lower_bound() чтобы заменить цикл for в моей версии resolve_lang(), но это немного неудобно в C ++ 98, поскольку вы не можете использовать лямбда для компаратора.

Рассмотрите возможность хранения строк перевода в std::strings

Вместо того, чтобы хранить строки перевода в массивах обычных строк C, вы также можете рассмотреть возможность хранения их в массивах std::stringс. Недостатком является то, что все они будут инициализированы один раз во время выполнения, но тогда вы можете просто вернуть ссылку на этот массив вместо копирования строк C в i_tr. Какой метод лучше всего зависит от того, сколько языков вы хотите поддерживать, и от того, как часто вы переключаете языки в коде, умноженных на количество строк на языке.

  • Компромисс – сделать массив string_view что позволяет избежать копирования байтов, но дает представление о фактической длине без сканирования терминатора. Как я отмечал в своем предыдущем ответе, в C ++ 98 вы можете включить такой класс как часть вашего проекта, даже если он не входит в вашу копию стандартной библиотеки, достаточно старую, чтобы пить.

    – JDłuosz

  • Я обязательно применю первые пункты (не уверен, где опубликовать обновление кода) относительно хранения всего в std::string Я думаю, что преимущество зависит от того, насколько часто происходит переключение между языками, в моем случае это происходит один раз при запуске.

    – MatG

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *