Классический способ локализации аддона Blender (перевода аддона на разные языки) удобен тем, что для него требуется всего лишь одно обращение к Blender python API – получение текущей используемой локали. Он хорош своей независимостью, однако Blender не был бы Blender-ом, если бы не предоставил пользователям возможность локализации аддонов через свой API.
Принцип создания мультиязычных аддонов с использованием Blender python API по сути мало отличается от классического. Нам точно так же необходимо создать словарь со всеми вариантами перевода текстовых строк, используемых в нашем аддоне, и в дальнейшем пользоваться им для перевода.
Для разбора на практике возьмем все тот же код простейшего аддона.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
from bpy.types import Panel, Operator from bpy.utils import register_class, unregister_class bl_info = { 'name': 'Localizatoin Test', 'category': 'All', 'version': (1, 0, 0), 'blender': (2, 91, 0), } class LOCALIZATION_TEST_OT_test(Operator): bl_idname = 'localization_test.test' bl_label = 'Operator name' def execute(self, context): print('Test text for printing in system console') return {'FINISHED'} class LOCALIZATION_TEST_PT_panel(Panel): bl_idname = 'LOCALIZATION_TEST_PT_panel' bl_label = 'Panel Header' bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'Localization test' def draw(self, context): layout = self.layout layout.label(text='Test text in Blender UI') layout.operator('localization_test.test', icon='BLENDER') def register(): register_class(LOCALIZATION_TEST_OT_test) register_class(LOCALIZATION_TEST_PT_panel) def unregister(): unregister_class(LOCALIZATION_TEST_PT_panel) unregister_class(LOCALIZATION_TEST_OT_test) if __name__ == '__main__': register() |
И сделаем для него локализацию с использованием Blender python API.
Для начала создадим словарь со всеми вариантами перевода текстовых строк, используемых в аддоне.
В отличие от классического метода, где мы сами определяли формат словаря так, как нам удобно, словарь, который мы будем использовать совместно с API Blender должен иметь определенную жесткую структуру.
В качестве первого ключа здесь необходимо использовать текстовое обозначение локали. Обратите внимание, что указывать отдельно вариант перевода на английский язык (локаль по умолчанию) нет необходимости.
Внутренний словарь для каждой локали в качестве ключа должен использовать кортеж, состоящий из двух элементов.
Первый элемент – контекст использования данного перевода. Он указывается, если одно понятие имеет различные переводы в зависимости от того, где оно используется. Например, одно и то же название, если оно используется как имя оператора может иметь один перевод, и совершенно другой, если оно используется, например в нодах шейдеров. Если перевод не зависит от контекста, в качестве значения нужно указывать “*”.
Второй элемент – собственно исходная текстовая строка на языке по умолчанию (обычно на английском). Именно эту строку и именно на этом языке вы должны будете использовать в дальнейшем в коде вашего аддона.
Значением элемента словаря, описанного таким составным ключом, будет перевод строки текста на язык указанной в верхнем ключе локали.
Например так будет выглядеть словарь переводов для одной текстовой строки “Test text in Blender UI” на испанский и японский языки:
1 2 3 4 5 6 7 8 |
langs = { 'es': { ('*', 'Test text in Blender UI'): 'Prueba de texto en la interfaz de usuario de Blender' }, 'ja_JP': { ('*', 'Test text in Blender UI'): 'Blender ユーザーインターフェースでテキストをテストする' } } |
Если бы мы обращались к нему напрямую, как в классическом методе локализации, нам нужно было бы писать что-то подобное:
1 2 |
langs['ja_JP'][('*', 'Test text in Blender UI')] # Blender ユーザーインターフェースでテキストをテストする |
Однако API Blender избавляет нас от необходимости дальнейшего обращения к словарю переводов напрямую.
Для того, чтобы API Blender получил доступ к предоставленному нами словарю и мог им пользоваться для переводов, наш словарь необходимо просто зарегистрировать в API.
Для этого внутри функции “register”, там где регистрируются все классы нашего аддона, необходимо добавить регистрацию словаря при помощи следующей команды:
1 2 3 4 |
import bpy def register(): bpy.app.translations.register(__name__, langs) |
где “langs” – имя созданного нами словаря переводов.
Для корректной работы API словарь так же необходимо разрегистрировать по окончании работы аддона в функции “unregister”:
1 2 |
def unregister(): bpy.app.translations.unregister(__name__) |
Теперь при переключении языка пользователем все текстовые строки в вашем аддоне, имеющие перевод на выбранный язык, будут автоматически переведены. И вам почти ничего не нужно менять в исходном коде аддона!
Использовать вызов функции, как мы делали в классическом методе, для перевода интерфейса не нужно, исходные строки нужно оставить в первоначальном виде простого присваивания.
1 |
bl_label = 'Panel Header' |
Однако для вызовов в коде, например в функции “print”, или для перевода строк с форматированием, как и в классической локализации, нужно присваивание текстовой константы в коде заменить на вызов специальной функции API – “pgettext”.
Например указание строки внутри функции “print”:
1 |
print('Test text for printing in system console') |
нужно заменить на вызов функции:
1 |
print(bpy.app.translations.pgettext('Test text for printing in system console')) |
Добавим для каждой текстовой строки нашего аддона переводы в словарь, сделанный в соответствии с требованиями API:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
langs = { 'es': { ('*', 'Test text in Blender UI'): 'Prueba de texto en la interfaz de usuario de Blender', ('Operator', 'Operator name'): 'Nombre del operador', ('*', 'Test text for printing in system console'): 'Prueba de texto para imprimir en la consola del sistema', ('*', 'Panel Header'): 'Encabezado del panel', ('*', 'Localization test'): 'Prueba de localización' }, 'ja_JP': { ('*', 'Test text in Blender UI'): 'Blender ユーザーインターフェースでテキストをテストする', ('Operator', 'Operator name'): 'オペレーター名', ('*', 'Test text for printing in system console'): 'システムコンソールで印刷するためのテストテキスト', ('*', 'Panel Header'): 'パネルヘッダー', ('*', 'Localization test'): 'ローカリゼーションテスト' } } |
Также поменяем код присваивания текстовых строк на вызов функции, только там, где это необходимо.
Теперь Blender отображает интерфейс нашего аддона в соответствии в текущим языком пользователя.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
import bpy from bpy.types import Panel, Operator from bpy.utils import register_class, unregister_class bl_info = { 'name': 'Localizatoin Test', 'category': 'All', 'version': (1, 0, 0), 'blender': (2, 91, 0), } langs = { 'es': { ('*', 'Test text in Blender UI'): 'Prueba de texto en la interfaz de usuario de Blender', ('Operator', 'Operator name'): 'Nombre del operador', ('*', 'Test text for printing in system console'): 'Prueba de texto para imprimir en la consola del sistema', ('*', 'Panel Header'): 'Encabezado del panel', ('*', 'Localization test'): 'Prueba de localización' }, 'ja_JP': { ('*', 'Test text in Blender UI'): 'Blender ユーザーインターフェースでテキストをテストする', ('Operator', 'Operator name'): 'オペレーター名', ('*', 'Test text for printing in system console'): 'システムコンソールで印刷するためのテストテキスト', ('*', 'Panel Header'): 'パネルヘッダー', ('*', 'Localization test'): 'ローカリゼーションテスト' } } class LOCALIZATION_TEST_OT_test(Operator): bl_idname = 'localization_test.test' bl_label = 'Operator name' def execute(self, context): print(bpy.app.translations.pgettext('Test text for printing in system console')) return {'FINISHED'} class LOCALIZATION_TEST_PT_panel(Panel): bl_idname = 'LOCALIZATION_TEST_PT_panel' bl_label = 'Panel Header' bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'Localization test' def draw(self, context): layout = self.layout layout.label(text='Test text in Blender UI') layout.operator('localization_test.test', icon='BLENDER') def register(): bpy.app.translations.register(__name__, langs) register_class(LOCALIZATION_TEST_OT_test) register_class(LOCALIZATION_TEST_PT_panel) def unregister(): unregister_class(LOCALIZATION_TEST_PT_panel) unregister_class(LOCALIZATION_TEST_OT_test) bpy.app.translations.unregister(__name__) if __name__ == '__main__': register() |
В отличие от рассмотренного ранее классического метода локализации, данный метод имеет более жесткие требования к организации словаря переводов и больше обращений к API, однако он удобнее тем, что не везде для перевода нужно явно вызывать специальную функцию, а весь интерфейс теперь переводится “на лету”, включая названия операторов и заголовки панелей.
Исходные файлы для этого урока доступны на Patreon.