Создавая новый проект, мне приходилось использовать либо *.resx для WinForms, либо I2Localization для Unity, либо другие решения для локализации приложений. Все эти решения похожи тем, что приходится придумывать ключ-локализации, вставлять его в код и в словарь. Поначалу все хорошо, но со временем этот процесс начинает раздражать. Вместе с тем, смотря на ключ в коде не всегда понятно о чем речь.

О ситуации когда нужно добавить локализацию в большой проект где её вообще не было, я даже говорить не буду как это сложно.

Не знаю почему, но оказывается существует уже давно такое готовое решение как gnu/gettext. Расспрашивая своих знакомых и коллег (тех кто работает с .NET), большинство даже и не слышал о таком. Поэтому решил поделиться с этим удобным инструментом.

Принцип прост. Вы пишите код со строками на английском языке, запускаете утилиту, которая сканирует исходники и предоставляет вам возможность перевода. Никаких ключей придумывать не надо. Текст на английском и есть ключ.

Приступим


1) Устанавливаем пакет NGettext через Nu-get:
PM> Install-Package NGettext

NGettext – кроссплатформенная реализация GNU/Gettext для .NET.

2) Добавляем в свой проект дополнительный файл, который немного упростит синтаксис:
https://github.com/neris/NGettext/blob/master/doc/examples/T.cs

Также добавляем директорию в проект, где будут хранится переводы:
MyProj\Loc\ru-RU\LC_Messages

В моем случае получается такая картина:



3) Добавляем пути в файл T.cs:

static T()
{
    var localesDir = Path.Combine(Directory.GetCurrentDirectory(), "Loc");
    _Catalog = new Catalog("Test", localesDir, new CultureInfo("ru-RU"));
}

Упрощено. Для примера только русский. (Есть возможность считывать словари из самой сборки)

4) Пишем свой код с использованием локализации. Вместо “text” пишем T._(“text”)

namespace TestCode
{
    static class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine(T._("Hello, World!"));
            Console.WriteLine(T._("Cat"));
            Console.ReadKey();
        }
    }
}

5) Теперь нам необходимо перевести весь наш текст. Качаем PoEdit. Создаем файл перевода:
Файл > Создать > папка LC_MESSAGES > Test.po



Указываем папку, в которой находятся наши исходники. Их программа будет сканировать:



Так же необходимо указать ключевое слово, которое будет искать poEdit для перевода:



Добавляем нужный нам перевод и сохраняем.



Добавляем файлы перевода в проект. Делаем их Copy always:
(Есть возможность встраивать их в саму сборку)



Готово. Запускаем:



Настройка готова. Далее все просто. Пишите код – правите перевод


Также можно найти готовые библиотеки для локализации интерфейсов:

> WPF
> Дополнительная информация об использовании NGettext
> Информация о GNU/Gettext

Комментарии (28)


  1. Leopotam
    11.12.2018 14:25

    Что произойдет со старой записью, когда поменяется английский текст-ключ в исходниках? Просто все потеряется и нужно будет заново выполнять локализацию? А если там просто фикс синтаксиса?


    1. GLeBaTi Автор
      11.12.2018 14:47

      Каждый ключ в *.po-файле ссылается на конкретный файл.
      gettext генерирует новый *.po-файл и делает какой-то умный merge со старым.
      Я пробовал менять несколько букв, изменять положение строки и т.п. Весь перевод остался, просто подсвечиваются измененные перед сохранением.


      1. Leopotam
        11.12.2018 14:50

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


        1. GLeBaTi Автор
          11.12.2018 14:54

          Контролируемая. Вам в редакторе подсветятся все изменения. Это как git merge делать.
          На среднем проекте у меня пока не возникало проблем. Интересно было бы услышать мнение людей использующих gettext в больших проектах


    1. yajohn
      11.12.2018 14:55

      Да, потеряется, но не все, а только один перевод. Костыль — псевдоперевод с английского на английский. Что иногда оправдано само по себе, поскольку исходники пишет программист, а переводит — профессиональный переводчик.


    1. immaculate
      12.12.2018 04:31

      Такие строки никуда не исчезают, а помечаются утилитами gettext флагом fuzzy. Потом можно отредактировать строку с учетом изменений, и снять флаг fuzzy.


      В нормальных инструментах для перевода (poEdit не пользовался около 10-12 лет, так что не помню, есть ли в нем это) есть еще память переводов, глоссарий, подсказки, и многое другое.


      Например, сейчас пользуюсь Weblate для перевода нескольких проектов. Изумительная вещь, и переводчикам ничего объяснять не приходится.


  1. lowercase
    11.12.2018 14:51
    +2

    А как поведет себя либа с омонимами? Ну то есть, например, есть у меня в исходном тексте два места в которых слово draw используется. Но в одном оно означает рисовать, а в другом — тянуть карты. Она позволит нормально в обоих местах перевести или тут будет коллизия?


    1. GLeBaTi Автор
      11.12.2018 14:53
      -2

      Использовать разные ключи. (Например draw_pic, draw_map)


      1. opxocc
        11.12.2018 15:01
        +1

        Вроде есть же еще понятие контекста? Я так понял что через `T._p(` можно использовать.


        1. GLeBaTi Автор
          11.12.2018 15:11

          да, поправил


      1. lowercase
        11.12.2018 15:02

        А, то есть, текст, который указывается в T._() — это сам ключ? Я подумал, что это одновременно, и ключ, и дефолтный текст.


        1. staticlab
          11.12.2018 15:06
          +1

          Лучше туда всё-таки ключ указывать во избежание таких эксцессов.


        1. GLeBaTi Автор
          11.12.2018 15:06

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


  1. mayorovp
    11.12.2018 15:22
    +1

    Как-то тут обошли вниманием кучу острых углов, которые могут испортить всю локализацию...


    Первое — омонимы и просто многозначные слова, уже упомянутые выше. Добавлю сюда также просто любые слишком короткие строки.


    Вот свежий пример из локализации Stack overflow: слово "about" может быть переведено как "о компании", "о сайте", "об участнике" и еще кучей разных способов. Просто потому что прямой перевод — просто предлог "о" — слишком мал для ссылки или заголовка.


    Второе — локализуемые сообщения никогда нельзя собирать конкатенацией. Только string.Format, и переводить надо форматную строку целиком.


    Третье — надо не забывать про согласование с числительными и оставлять переводчику достаточно инструментов для этих целей.


    1. vp1000
      11.12.2018 17:07

      Сам уже много лет пользуюсь локализацией gettext + poEdit для Windows приложений. Хоть и не .net.
      1. Это не проблема. Всегда считаем что есть перевод с английского на английский и другие необходимые языки. Короткие слова и омонимы просто писать в виде развернутого ключа. Например «About company» а в переводе уже указывать нужный вариант.
      2. Это действительно может быть проблемой. Сборку строк нужно производить не командой не format, а например записью в поток std::stringstream частей, каждая из которых переводится отдельно.
      3. Вот переводчики самая большая проблема. Они не могу понять как пользоваться poEdit. Что нельзя нарушать форматирование строк. Что можно менять, а что нельзя. Приходится садиться и править текст вместе с переводчиком, а потом тестировать результат и править еще раз.
      Изменения в коде править легко. poEdit подсвечивает все изменения и может предлагать варианты из готового словаря.


      1. mayorovp
        11.12.2018 17:14
        +2

        Сборку строк нужно производить не командой не format, а например записью в поток std::stringstream частей, каждая из которых переводится отдельно.

        Нет, нельзя так делать! Никакого "переводится отдельно"! У переводчика должна быть возможность задать формат для подставляемых значений, переставить части местами, или даже продублировать любую из них. Ничего из этого std::stringstream не дает.


        3. Вот переводчики самая большая проблема. Они не могу понять как пользоваться poEdit. Что нельзя нарушать форматирование строк.

        Или вы не предоставили достаточных инструментов, из-за чего переводчикам теперь приходится подбирать структуру предложения под шаблон?


      1. ingumsky
        11.12.2018 17:25
        +2

        Вот переводчики самая большая проблема. Они не могу понять как пользоваться poEdit. Что нельзя нарушать форматирование строк. Что можно менять, а что нельзя. Приходится садиться и править текст вместе с переводчиком, а потом тестировать результат и править еще раз.
        Не надо наговаривать на переводчиков. Если ваши переводчики не умеют пользоваться инструментом, с помощью которого вы хотите делать свою локализацию, это ваша проблема как заказчика — вероятно, вы пожалели денег на квалифицированных специалистов и/или не уделили внимание тому, соответствуют ли подрядчики вашим требованиям. Если вы сознательно берёте людей, зная, что они не владеют инструментом/технологией, которую вы используете, ваша задача — их научить и ответить на все их вопросы. В противном случае вы сам себе злобный буратино.


        1. vp1000
          12.12.2018 10:01
          -1

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


          1. ingumsky
            12.12.2018 14:25
            +1

            У вас противоречие — переводчики загружены вашей работой, но у них нет времени научиться использованию вашей технологии локализации. Так не бывает. Тем более, что gettext — распространённая технология, не требующая особой подготовки. Либо у вас переводчики — совсем не фонтан, либо вы «не умеете их готовить». Хорошие переводчики — прекрасно обучаемые специалисты, владеющие большим спектром технологий и разнообразным ПО, причём им неважно, какое именно ПО использует заказчик — перестроиться не составит проблемы. Многие способны частично автоматизировать свою работу, написав для этого скрипты и т.п. Это я вам говорю, как человек, который работает в области перевода и локализации 15 лет.
            PS Минус не мой.


            1. vp1000
              12.12.2018 17:26

              Переводчики загружены не моей работой, а работой компании. Переводом контрактов, переписки с заказчиками, тех. документации и пр. Локализация ПО для них разовая и не основная работа. Из за этого и проблемы.


              1. ingumsky
                13.12.2018 00:00

                ОК, так понятнее. Тогда, если позволите, мой совет:
                Организуйте правильно работу с локализацией, займитесь этим всерьёз. Есть ощущение, что сейчас вы разгребаете проблемы, вызванные неправильной организацией. Я не знаю точно, как процесс организован у вас, поэтому рекомендации будут достаточно общими — давайте на перевод законченные строки, желательно с контекстом, чтобы переводчик смог понять и передать всё правильно. Не старайтесь добиться «атомарных значений» фрагментов — чем крупнее фрагмент для перевода, тем более точным и соответствующим оригиналу будет перевод. Не забывайте о том, что в разных языках могут быть различные правила грамматики (дополнительные времена, падежи, словоформы, формы множественного числа и части речи) и разные правила типографики, поэтому, с одной стороны, обеспечьте переводчику возможность «манёвра», с другой, оставьте порядок следования слов на усмотрение переводчика. Ну и, разумеется, не забывайте о том, что в разных языках одна и та же фраза может значительно отличаться по длине, поэтому должен быть запас по ограничению символов.


      1. Johan
        11.12.2018 19:54
        +2

        1. Боюсь, вам не удастся угадать все случаи, когда одно и то же английское слово переводится по разному в зависимости от контекста. К тому же, использование развёрнутого ключа, в моём понимании, уже стремится к тем самым идентификаторам, от которых хотел избавиться автор.
        2. Переводы частей отдельно, возможно, и являются причиной проблем с переводчиками. Языки не всегда подчиняются законам, которые кажутся логичными. Законченный текст должен переводится целиком без всяких склеек.


      1. gdt
        12.12.2018 03:21
        +1

        Вам, наверное, никогда не приходилось иметь дела с арабским языком.


        1. vp1000
          12.12.2018 10:06

          Пока не приходилось ни с арабским ни с китайским. Целевые языки заранее известны. Пытаюсь адаптировать фразы в в приложении для универсального использования во всех языках. Пока получалось. Хотя я согласен со всеми комментаторами указывающими на недостатки моего подхода.


          1. gdt
            12.12.2018 10:12

            Проблема арабского языка — в написании справа налево. А когда в дело вступает форматирование (я имею ввиду подстановку цифр, дат, денежных значений и т. д.) — становится совсем невесело.
            Лично я не против вашего подхода, но на мой взгляд он менее универсальный, конечно это не смертельно, если вы не планируете расширять список используемых языков.


            1. vp1000
              12.12.2018 10:36

              Оборудование, в состав которого входит обсуждаемое ПО, поставлялось в том числе и в ОАЭ с английским языком интерфейса. Требований по локализации заказчик не выдвигал. Поэтому с этими проблемами и не столкнулся.


    1. immaculate
      12.12.2018 04:25

      В gettext (вообще, не знаю насчет данной конкретной реализации) естественно есть инструменты для согласования числительных (ngettext) и для добавления контекста к строке для переводчика. В более развитых редакторах, чем упоминаемый poedit, есть также дополнительные средства, в частности, к строкам можно прикреплять скриншоты приложения, чтобы переводчики видели, где именно используется строка.


  1. alex1t
    12.12.2018 12:27
    +2

    Тоже работаем с NGetText. В принципе всё устраивает. Работа с числительными там кстати есть — формат gettext это поддерживает. В коде вместе с текстом передается число (n) — для которого мы хотим получить форму. В целевом языке перевода (*.po-файле) задаётся правило для числительных, которое по сути по формуле вычисляет «категорию» числительной формы. Например в английском — всего две категории (day/days), а в русском уже 3 (2 дня/ 5 дней/ 21 день). В файле перевода соответственно и пишутся по 3 варианта фразы с разными словоформами множественного числа + одна словоформа единственного числа