From 8e5a89a5a70103ef5871e6f402f3cdd5c5d80b79 Mon Sep 17 00:00:00 2001 From: Hleb Valoshka <375gnu@gmail.com> Date: Sun, 29 Aug 2021 12:49:18 +0400 Subject: [PATCH] [win] build DLLs with UI translation --- .github/workflows/ci.yml | 2 +- cmake/windres.cmake | 52 +++++++ po/translate_resources.pl | 193 ++++++++++++++------------ src/celestia/win32/CMakeLists.txt | 4 +- src/celestia/win32/res/CMakeLists.txt | 8 ++ 5 files changed, 165 insertions(+), 94 deletions(-) create mode 100644 cmake/windres.cmake create mode 100644 src/celestia/win32/res/CMakeLists.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index effe871ca..8ba869460 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,7 +66,7 @@ jobs: working-directory: ${{github.workspace}}/build/src/celestia run: | 7z a celestia-dep.${{matrix.platform}}.7z ${{env.BUILD_TYPE}}\* - 7z a celestia-win.${{matrix.platform}}.7z win32\${{env.BUILD_TYPE}}\* + 7z a celestia-win.${{matrix.platform}}.7z win32\${{env.BUILD_TYPE}}\* win32\res\${{env.BUILD_TYPE}}\res_*.dll c:\vcpkg\ports\qt5-base\qtdeploy.ps1 qt\${{env.BUILD_TYPE}}\ 7z a celestia-qt.${{matrix.platform}}.7z qt\${{env.BUILD_TYPE}}\* cd ..\tools diff --git a/cmake/windres.cmake b/cmake/windres.cmake new file mode 100644 index 000000000..97409f7c7 --- /dev/null +++ b/cmake/windres.cmake @@ -0,0 +1,52 @@ +function(_WINDRES_GET_UNIQUE_TARGET_NAME _name _unique_name) + set(propertyName "_WINDRES_UNIQUE_COUNTER_${_name}") + get_property(currentCounter GLOBAL PROPERTY "${propertyName}") + if(NOT currentCounter) + set(currentCounter 1) + endif() + set(${_unique_name} "${_name}_${currentCounter}" PARENT_SCOPE) + math(EXPR currentCounter "${currentCounter} + 1") + set_property(GLOBAL PROPERTY ${propertyName} ${currentCounter} ) +endfunction() + +macro(WINDRES_CREATE_TRANSLATIONS _rcFile _firstPoFileArg) + # make it a real variable, so we can modify it here + set(_firstPoFile "${_firstPoFileArg}") + + set(_addToAll) + if(${_firstPoFile} STREQUAL "ALL") + set(_addToAll "ALL") + set(_firstPoFile) + endif() + + set(_dllFiles) + foreach(_currentPoFile ${_firstPoFile} ${ARGN}) + get_filename_component(_absFile ${_currentPoFile} ABSOLUTE) + get_filename_component(_lang ${_absFile} NAME_WE) + set(_locRcFile ${CMAKE_CURRENT_BINARY_DIR}/celestia_${_lang}.rc) + set(_dllFile res_${_lang}) + + add_custom_command( + OUTPUT ${_locRcFile} + COMMAND perl ${CMAKE_SOURCE_DIR}/po/translate_resources.pl ${_lang} + DEPENDS ${_absFile} + ) + + add_library(${_dllFile} MODULE ${_locRcFile}) + set_target_properties(${_dllFile} PROPERTIES PREFIX "" LINKER_LANGUAGE C) + if(MSVC) + set_target_properties(${_dllFile} PROPERTIES LINK_FLAGS "/MANIFEST:NO /NODEFAULTLIB /NOENTRY") + endif() + + install(TARGETS ${_dllFile} LIBRARY DESTINATION ${CMAKE_INSTALL_BINDIR}) + set(_dllFiles ${_dllFiles} ${_dllFile}) + endforeach() + + if(NOT TARGET resources) + add_custom_target(resources) + endif() + + _WINDRES_GET_UNIQUE_TARGET_NAME(resources uniqueTargetName) + add_custom_target(${uniqueTargetName} ${_addToAll} DEPENDS ${_dllFiles}) + add_dependencies(resources ${uniqueTargetName}) +endmacro() diff --git a/po/translate_resources.pl b/po/translate_resources.pl index 1eabd2e6a..fdef7979b 100755 --- a/po/translate_resources.pl +++ b/po/translate_resources.pl @@ -3,33 +3,29 @@ ################################################################ # Utility for i18n on Win32 # -# Author: Christophe Teyssier -# date: July 2006 +# Copyright: 2006-2021, Celestia Development Team +# Original author: Christophe Teyssier +# Original date: July 2006 # -# - Loads translations from the po files and produces translated .rc files in src/celestia/res -# - Produces a dll with the translated resources for each po file in the locale/ dir -# - Produces a list of unicode codepoints for each language in the current dir -# (to generate txf font textures) -# - Compiles .po files and installs catalogs under locale/ -# -# Requirements: -# - rc.exe link.exe and msgfmt.exe must be in the PATH +# - Loads translations from the po files and produces translated .rc +# files in src/celestia/win32/res ################################################################ use Encode; use File::Basename; +use File::Path qw(make_path); +use Cwd qw(getcwd); -my $dir = dirname $0; -my $resource_file = "$dir/../src/celestia/win32/res/celestia.rc"; -my $output_dir = "$dir/../src/celestia/win32/res/"; +my $lang = $ARGV[0]; -opendir(DIR, $dir); -my @po_files = sort (grep( /\.po$/, readdir(DIR) )); -closedir DIR; +my $po_dir = dirname $0; +my $resource_file = "$po_dir/../src/celestia/win32/res/celestia.rc"; +my $rc_dir = dirname $resource_file; +my $build_dir = getcwd; open RES, "< $resource_file"; my $resource; -{ +{ local $/; $resource = ; } @@ -38,89 +34,92 @@ close RES; # LangID and code page # http://msdn.microsoft.com/en-us/library/dd318693 my %lang = ( -ar => [ '401', 1256 ], -be => [ '423', 1251 ], -bg => [ '402', 1251 ], -de => [ '407', 1252 ], -el => [ '408', 1253 ], -en => [ '409', 1252 ], -es => [ '40a', 1252 ], -fr => [ '40c', 1252 ], -gl => [ '456', 1252 ], -hu => [ '40e', 1250 ], -it => [ '410', 1252 ], -ja => [ '411', 932 ], -ko => [ '412', 949 ], -lt => [ '427', 1257 ], -lv => [ '426', 1257 ], -nl => [ '413', 1252 ], -no => [ '414', 1252 ], -pl => [ '415', 1250 ], -pt_BR => [ '416', 1252 ], -pt => [ '816', 1252 ], -ro => [ '418', 1250 ], -ru => [ '419', 1251 ], -sk => [ '41b', 1250 ], -sv => [ '41d', 1252 ], -tr => [ '41f', 1254 ], -uk => [ '422', 1251 ], -zh_CN => [ '804', 936 ], -zh_TW => [ '404', 950 ], +ar => [ '401', 1256, 'LANG_ARABIC, SUBLANG_DEFAULT'], +be => [ '423', 1251, 'LANG_BELARUSIAN, SUBLANG_DEFAULT'], +bg => [ '402', 1251, 'LANG_BULGARIAN, SUBLANG_DEFAULT'], +de => [ '407', 1252, 'LANG_GERMAN, SUBLANG_DEFAULT'], +el => [ '408', 1253, 'LANG_GREEK, SUBLANG_DEFAULT'], +en => [ '409', 1252, 'LANG_ENGLISH, SUBLANG_DEFAULT'], +es => [ '40a', 1252, 'LANG_SPANISH, SUBLANG_DEFAULT'], +fr => [ '40c', 1252, 'LANG_FRENCH, SUBLANG_DEFAULT'], +gl => [ '456', 1252, 'LANG_GALICIAN, SUBLANG_DEFAULT' ], +hu => [ '40e', 1250, 'LANG_HUNGARIAN, SUBLANG_DEFAULT'], +it => [ '410', 1252, 'LANG_ITALIAN, SUBLANG_DEFAULT'], +ja => [ '411', 932, 'LANG_JAPANESE, SUBLANG_DEFAULT'], +ko => [ '412', 949, 'LANG_KOREAN, SUBLANG_DEFAULT'], +lt => [ '427', 1257, 'LANG_LITHUANIAN, SUBLANG_DEFAULT'], +lv => [ '426', 1257, 'LANG_LATVIAN, SUBLANG_DEFAULT'], +nb => [ '414', 1252, 'LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL'], +nl => [ '413', 1252, 'LANG_DUTCH, SUBLANG_DEFAULT'], +no => [ '814', 1252, 'LANG_NORWEGIAN, SUBLANG_NORWEGIAN_NYNORSK'], +pl => [ '415', 1250, 'LANG_POLISH, SUBLANG_DEFAULT'], +pt_BR => [ '416', 1252, 'LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN'], +pt => [ '816', 1252, 'LANG_PORTUGUESE, SUBLANG_PORTUGUESE_PORTUGAL'], +ro => [ '418', 1250, 'LANG_ROMANIAN, SUBLANG_DEFAULT'], +ru => [ '419', 1251, 'LANG_RUSSIAN, SUBLANG_DEFAULT'], +sk => [ '41b', 1250, 'LANG_SLOVAK, SUBLANG_DEFAULT'], +sv => [ '41d', 1252, 'LANG_SWEDISH, SUBLANG_DEFAULT'], +tr => [ '41f', 1254, 'LANG_TURKISH, SUBLANG_DEFAULT'], +uk => [ '422', 1251, 'LANG_UKRAINIAN, SUBLANG_DEFAULT'], +zh_CN => [ '804', 936, 'LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL'], +zh_TW => [ '404', 950, 'LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED'], ); -mkdir "$dir\\..\\locale" if ! -d "$dir\\..\\locale"; +my @keywords = qw{ + POPUP + MENUITEM + DEFPUSHBUTTON + LTEXT + CAPTION + PUSHBUTTON + RTEXT + CTEXT + LTEXT + GROUPBOX + CONTROL + GROUPBOX + AUTOCHECKBOX + AUTORADIOBUTTON +}; -my %codepoints; # hash holds unicode codepoints used by language +my $keys = join('|', @keywords); -foreach my $po (@po_files) { - my $strings = load_po_strings("$dir\\$po"); - my $res = $resource; - my $lang = basename $po; - $lang =~ s/\..*//o; - mkdir "$dir\\..\\locale\\$lang" if ! -d "$dir\\..\\locale\\$lang"; - mkdir "$dir\\..\\locale\\$lang\\LC_MESSAGES" if ! -d "$dir\\..\\locale\\$lang\\LC_MESSAGES"; +my $strings = load_po_strings("$po_dir/$lang.po"); +my @res = split(/\n|\r\n/, $resource); +my $lang_id = $lang{$lang}[0]; +my $codepade = $lang{$lang}[1]; +my $lang_name = $lang{$lang}[2]; +Encode::from_to($v, 'UTF-8', "CP$codepade"); - while (my ($k, $v) = each %$strings) { - map { $codepoints{$lang}{$_} = 1; } map { sprintf '%04X', ord($_); } split //, Encode::decode_utf8($v); - Encode::from_to($v, 'UTF-8', "CP$lang{$lang}[1]"); - map { $c{ord($_)} = 1; } split //, Encode::decode("UTF-8", $v); - $res =~ s/"\Q$k\E"/"$v"/g; +while (my ($k, $v) = each %$strings) { + next if $k !~ /^(.+)\004(.+)$/; + my $msgctxt = $1; + my $msgid = $2; + + foreach my $line (@res) { + if ($line =~ /^\s*(?:$keys)/) { + $line =~ s/\bNC_\(\s*"\Q$msgctxt\E"\s*,\s*"\Q$msgid\E"\s*\)/"$v"/g; + } } - - $res=~ s/\Q#pragma code_page(1252)\E/#pragma code_page($lang{$lang}[1])/; - open OUT, "> $output_dir/celestia_$lang.rc"; - print OUT $res; - close OUT; - system qq{rc /l $lang{$lang}[0] /d "NDEBUG" /fo $dir\\..\\src\\celestia\\win32\\celestia_$lang.res /i "$dir\\..\\src\\celestia\\win32\\res" $dir\\..\\src\\celestia\\win32\\res\\celestia_$lang.rc}; - system qq{link /nologo /noentry /dll /machine:I386 /out:$dir\\..\\locale\\res_$lang.dll $dir\\..\\src\\celestia\\win32\\celestia_$lang.res}; - system qq{msgfmt $dir\\$lang.po -o $dir\\..\\locale\\$lang\\LC_MESSAGES\\celestia.mo}; } -opendir(DIR, "$dir\\..\\po2"); -my @po_files = sort (grep( /\.po$/, readdir(DIR) )); -closedir DIR; -foreach my $po (@po_files) { - my $lang = basename $po; - $lang =~ s/\..*//o; - - my $strings = load_po_strings("$dir\\..\\po2\\$po"); - while (my ($k, $v) = each %$strings) { - map { $codepoints{$lang}{$_} = 1; } map { sprintf '%04X', ord($_); } split //, Encode::decode_utf8($v); +while (my ($k, $v) = each %$strings) { + next if $k =~ /^(.+)\004(.+)$/; + foreach my $line (@res) { + if ($line =~ /^\s*(?:$keys)/) { + $line =~ s/"\Q$k\E"/"$v"/g; + } } - - mkdir "$dir\\..\\locale\\$lang" if ! -d "$dir\\..\\locale\\$lang"; - mkdir "$dir\\..\\locale\\$lang\\LC_MESSAGES" if ! -d "$dir\\..\\locale\\$lang\\LC_MESSAGES"; - system qq{msgfmt $dir\\..\\po2\\$lang.po -o $dir\\..\\locale\\$lang\\LC_MESSAGES\\celestia_constellations.mo}; } -foreach my $lang (keys %codepoints) { - # list of unicode codepoints to generate font textures - my $chr; - map { $chr .= "$_ \n"; } sort keys %{$codepoints{$lang}}; - open CHR, "> $dir/codepoints_$lang.txt"; - print CHR $chr; - close CHR; +foreach my $line (@res) { + $line =~ s/LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US/\/\/LANGUAGE $lang_name/; + $line =~ s/\Q#pragma code_page(1252)\E/#pragma code_page($codepade)/; + $line =~ s/VALUE "Translation", 0x0?409, (?:1252|0x04B0)/VALUE "Translation", 0x$lang_id, $codepade/; } +open OUT, "> celestia_$lang.rc"; +print OUT join("\r\n", @res); +close OUT; sub load_po_strings { my $po = shift; @@ -130,18 +129,28 @@ sub load_po_strings { my $l1; my $l2; my $l3; - while ($l3 = ) { + my $l4; + while ($l4 = ) { # The po file is read by groups of three lines. # we can safely ignore multiline msgids since resource files # use only single line strings - if ($l2 =~ /^msgid\s+"(.*)"/) { + if ($l3 =~ /^msgid\s+"(.*)"/) { my $msgid = $1; - if ($l3 =~ /^msgstr\s+"(.+)"/ && $l1 !~ /fuzzy/ ) { - $strings{$msgid} = $1; + if ($l4 =~ /^msgstr\s+"(.+)"/) { + my $translation = $1; + if ($l2 =~ /^msgctxt\s+"(.+)"/) { + my $context = $1; + if ($l1 !~ /fuzzy/) { + $strings{"$context\004$msgid"} = $translation; + } + } elsif ($l2 !~ /fuzzy/) { + $strings{$msgid} = $translation; + } } } $l1 = $l2; $l2 = $l3; + $l3 = $l4; } close PO; return \%strings; diff --git a/src/celestia/win32/CMakeLists.txt b/src/celestia/win32/CMakeLists.txt index 6aed5747f..d2404c3ee 100644 --- a/src/celestia/win32/CMakeLists.txt +++ b/src/celestia/win32/CMakeLists.txt @@ -1,4 +1,4 @@ -if(NOT WIN32 OR NOT ENABLE_WIN) +if(NOT ((WIN32 OR MINGW) AND ENABLE_WIN)) message(STATUS "Either not Windows or Windows frontend is disabled.") return() endif() @@ -47,3 +47,5 @@ add_executable(celestia-win WIN32 ${WIN32_SOURCES} ${RESOURCES}) add_dependencies(celestia-win celestia) target_link_libraries(celestia-win celestia) install(TARGETS celestia-win RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +add_subdirectory(res) diff --git a/src/celestia/win32/res/CMakeLists.txt b/src/celestia/win32/res/CMakeLists.txt new file mode 100644 index 000000000..d2019c701 --- /dev/null +++ b/src/celestia/win32/res/CMakeLists.txt @@ -0,0 +1,8 @@ +if(NOT ENABLE_NLS) + return() +endif() + +include(windres) + +file(GLOB PO_FILES2 "${CMAKE_SOURCE_DIR}/po/*.po") +WINDRES_CREATE_TRANSLATIONS(celestia.rc ALL ${PO_FILES2})