C++: как локализовать уже написанную программу

Я хочу локализовать программу, которую я уже написал. Она довольно большая (почти 50 тысяч строк), и в идеале мне нужна система, которая позволяет мне (программисту) выполнять наименьший объем работы и без серьезных изменений в программе - если можно вообще никак.

Я посмотрел на gettext(), и он мне очень понравился, но мне непонятно, как он будет переводить такие строки:

const char *Colors[] = {
 { "Red" },
 { "Blue" },
 { "Yellow" },
 ....
};

которые ОЧЕНЬ часто встречаются в моей программе. Здесь замена "Red" на gettext("Red") явно не сработает.

Поэтому я подумал, что сделаю что-то вроде OutputFunction(gettext(Colors[Id])), но тогда как я могу получить список строк для локализации? Я сомневаюсь, что какая-либо программа достаточно умна, чтобы иметь возможность статически получить «Красный», «Синий», «Желтый» из этого списка локализации.

Поскольку это в основном сервер, нет необходимости в возможности изменять язык без перекомпиляции (я могу скомпилировать его для любого поддерживаемого языка без каких-либо серьезных проблем или раздражения), я подумал о constexpr C++0x, который был бы идеальным! Это будет работать в массивах и т. Д., И я легко получу список строк для локализации во время компиляции. Жаль, что ни один компилятор еще не реализовал это.

Изменение всех строк на идентификатор не вариант, поскольку это потребовало бы огромного количества работы с моей стороны, и особенно создание нового идентификатора для каждой новой строки было бы чертовски раздражающим. То же самое относится к преобразованию всех массивов, подобных приведенному выше, во что-то еще.

Итак, есть идеи? :/


person Thomas Bonini    schedule 14.09.2009    source источник
comment
Не то, чтобы это помогло вам, но просто в стороне, даже если бы constexpr был доступен, я сомневаюсь, что это помогло бы вам. constexpr работает только в том случае, если сама функция может быть оценена во время компиляции, поэтому, если ваша измененная gettext основана на открытии и чтении файлов локализации, вы все равно не сможете сделать ее constexpr (поскольку открытие и чтение файлов не может быть выполнено в время компиляции). Учитывая, что большинство таких систем основано на этом, я сомневаюсь, что constexpr действительно будет настолько полезным.   -  person GRB    schedule 14.09.2009


Ответы (2)


Для вашего конкретного примера я мог бы попробовать что-то вроде:

// presumably globals
const char *Colors_en[] = {
 { "Red" },
 { "Blue" },
 { "Yellow" },
 ....
};
const char *Colors[] = {0};

// in main()
gettextarray(Colors_en, Colors, sizeof(Colors_en) / sizeof(char*));

gettextarray вызывает gettext для каждого ввода и записывает вывод. Я думаю, это можно было бы реализовать просто как вызов std::transform. И вы могли бы избежать параметра размера с помощью небольшого обмана шаблона.

Другим вариантом является вызов gettext в точке, где будет использоваться любая из строк цвета (отображается или добавляется к строке для отображения). Это означает изменение большего количества кода, но не требует, чтобы функция main() переводила каждый набор строк в программе до выполнения каких-либо действий, которые могут их использовать.

Если вы не хотите выполнять работу в main, вы можете сделать это в коде, использующем строки, примерно так:

if (Colors[0] == 0)
  gettextarray(Colors_en, Colors, sizeof(Colors_en) / sizeof(char*));

Или, если ваше приложение является многопоточным, рассмотрите возможность использования pthread_once или его эквивалента в API потоков, который вы используете.

person Steve Jessop    schedule 14.09.2009

После долгих экспериментов с gettext() и xgettext я думаю, что нашел способ сам (извините друг за друга, но мне не понравился ваш подход. Таких массивов должно быть сотни, и мне пришлось бы импортировать их все в main(), это много внешних и много дополнительной работы :/).

В любом случае, я думаю, что теоретически это можно сделать (я еще не пробовал переводить, но не понимаю, почему это не сработает)

Два #define:

#define _ gettext
#define __(x) x

Затем вы используете _ для фактического перевода и __, чтобы просто пометить строки как «переведенные»:

const char *Colors[] = {
 { __("Red") },
 { __("Blue") },
 { __("Yellow") },
 ....
};

void PrintColor(int id) {
    cout << _("The color is: ") << _(Colors[id]);
}

Затем вы запускаете:

xgettext -k_ -k__ *.cpp

И вы получите следующий файл .po:

#: test.cpp:2
msgid "Red"
msgstr ""

#: test.cpp:3
msgid "Blue"
msgstr ""

#: test.cpp:4
msgid "Yellow"
msgstr ""

#: test.cpp:9
msgid "The color is: "
msgstr ""

Итак, вы используете __ (или любое другое имя, не имеет большого значения) как «фиктивную функцию», чтобы просто сообщить xgettext, что строку нужно перевести, и _ для фактического вызова gettext( ).

Если вы вызываете _ со строкой, то строка также будет помечена как подлежащая переводу, если вы вызываете ее с помощью переменной, массива и т. д., тогда xgettext просто игнорирует ее.

Здорово! Теперь все, что мне нужно сделать, это просмотреть 5 триллионов файлов и добавить символы подчеркивания, как если бы я был обезьяной :/

person Thomas Bonini    schedule 14.09.2009