How LangString not translated can use a default language? - nsis

My installation must be available for many languages but I cannot translate absolutely all messages. How can I use the a default language when a message has not been translated?
For example:
!insertmacro MUI_LANGUAGE "English"
!insertmacro MUI_LANGUAGE "Spanish"
!insertmacro MUI_LANGUAGE "TradChinese"
!insertmacro MUI_LANGUAGE "SimpChinese"
LangString EMBEDED ${LANG_ENGLISH} "Installation single user"
LangString EMBEDED ${LANG_SPANISH} "Instalación usuario único"
I would like that if installation is in TradChinese, EMBEDED string would use the english translation because there is not EMBEDED for chinese.

I asked the same question on NSIS forum without success. Finally I created an small Perl script that parses my NSIS code and detect included languages and all Langstrings, detect missing translations, and create Langstrings initialized to the default language:
#!/usr/bin/perl
#
# filltranslationgaps.pl
# Get list of included languages
my $tmp = uc(`grep "^!insertmacro MUI_LANGUAGE" installer.nsi |grep -v ";" |cut -f2 -d\\"`);
my #lang = split(/\n/, $tmp);
my $DEFAULTLANGUAGE = $lang[0];
# Get langstrings
open( $fh, "<installer.nsi");
while (<$fh>) {
if (/LangString *(.*?) *\${LANG_(.*?)} *\"(.*)\"/) {
$phrases{$1}{$2}=$3;
}
}
close $fh;
# Foreach Langstring, check if it's defined in all included languages.
foreach my $k (keys %phrases) {
foreach my $j (#lang) {
if (not defined $phrases{$k}{$j}) {
#print "MISSING PAIR: $k\t$j\n";
print "LangString $k \${LANG_$j} \"$phrases{$DEFAULTLANGUAGE}{$j}\"\n";
}
}
}
My compile script:
#/bin/sh
perl filltranslationgaps.pl >missingphrases.nsi
makensis installer.nsi
In installer.nsi I need to include the generated nsi:
!include missingphrases.nsi
Hope it helps.

LangFile.nsh contains helper code to create language files with fallback support (Used by MUI etc) but each language has to be in a separate .nsh file.
If you want to keep all the translations in the .nsi you have to make your own custom macros:
!macro MyLangInit lnam nlf
!ifndef MYLANG
!define MYLANG ${lnam}
!ifndef MYLANG_${lnam}
!define MYLANG_${lnam}
LoadLanguageFile "${NSISDIR}\Contrib\Language files\${nlf}.nlf"
!ifndef LANG_${lnam}
!define LANG_${lnam} ${LANG_${nlf}}
!endif
!endif
!endif
!macroend
!define MyLangInit "!insertmacro MyLangInit "
!macro MyLangSet mode nam str
!ifndef MYLANG_${MYLANG}_${nam}
!define MYLANG_${MYLANG}_${nam} "${str}"
!else
!if ${mode} != 0
!ifdef MYLANG_${MYLANG}_${nam}
LangString ${nam} "" "${MYLANG_${MYLANG}_${nam}}"
!undef MYLANG_${MYLANG}_${nam}
!endif
!endif
!endif
!macroend
!macro MyLangLoad lnam
!define MyLangSet "!insertmacro MyLangSet 0 "
!insertmacro ${lnam}
!insertmacro ${MYLANG_FALLBACK}
!undef MyLangSet
!define MyLangSet "!insertmacro MyLangSet 1 "
!insertmacro ${lnam}
!insertmacro ${MYLANG_FALLBACK}
!undef MyLangSet
!undef MYLANG
!macroend
!macro English
${MyLangInit} English English
${MyLangSet} foo "bar"
${MyLangSet} bar "baz"
!macroend
!macro Bork
${MyLangInit} Bork Swedish ; Based on Swedish
${MyLangSet} foo "bork"
; Missing: ${MyLangSet} bar "barkz"
!macroend
!macro Danish
${MyLangInit} Danish Danish
; All strings are missing!
!macroend
!define MYLANG_FALLBACK English ; Language to use for missing strings
!insertmacro MyLangLoad Bork
!insertmacro MyLangLoad Danish
!insertmacro MyLangLoad English
Function .onInit
StrCpy $LANGUAGE ${LANG_BORK} ; TODO: Use LangDLL or some algorithm to select the language here
FunctionEnd
Section
SetDetailsView show
DetailPrint LangId=$LANGUAGE
DetailPrint "$(foo)"
DetailPrint "$(bar)"
SectionEnd

Related

How to fix "CreateDirectory: Relative paths not supported" when using a variable?

As a newbiew, I am still in the stage of experimenting and building little prototypes. The idea is to build a silent installer that has all settings in multiple sections of a .INI and the users calls the setup with parameter /config={NameOfSection}.
My current situation:
FooBar-install.ini
[PROD]
FOOHOME=c:\FooBar
FooBar.nsi
!include FileFunc.nsh
!include LogicLib.nsh
!insertmacro GetParameters
!insertmacro GetOptions
var /GLOBAL config
var /GLOBAL cmdLineParams
var /global REGAPPKEY
var /global FOOHOME
!define TheName "FooBar"
!define OutFileSuffix "-Install."
!define IniFile "$EXEDIR\${TheName}${OutFileSuffix}ini"
Name "${TheName} ${PRODUCT_VERSION}" ; bei 2 Kunden geht's auch kd-spezifisch ;)
OutFile ${TheName}${OutFileSuffix}exe
RequestExecutionLevel admin
Icon "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"
UninstallIcon "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
Section "-aInstaller Section"
ReadINIStr $FOOHOME ${IniFile} $config "FOOHOME"
MessageBox MB_OK "ini=${IniFile} , config=$config, FOOHOME=$FOOHOME"
CreateDirectory "SFOOHOME"
SectionEnd
function .onInit
UserInfo::GetAccountType
pop $0
${If} $0 != "admin" ;Require admin rights on NT4+
MessageBox mb_iconstop "Administrator rights required!"
SetErrorLevel 740 ;ERROR_ELEVATION_REQUIRED
${Else}
MessageBox MB_OK "onInit"
${EndIf}
; Get parameters
${GetParameters} $cmdLineParams
; /? param (help)
ClearErrors
${GetOptions} $cmdLineParams '/?' $R0
IfErrors +3 0
MessageBox MB_OK "Befehlszeilenparameter /config={{name}} verweist auf einen Abschnitt aus ${TheName}${OutFileSuffix}ini mit div. Parametern zur Steuerung des Setup"
Abort
Call parseParameters
Pop $R0
FunctionEnd
Function parseParameters
; /config
${GetOptions} $cmdLineParams '/config=' $R0
${If} ${Errors}
StrCpy $config "errPROD"
${Else}
StrCpy $config $R0
${Endif}
FunctionEnd
Problem
If I try to compile this, I get the msg
CreateDirectory: Relative paths not supported
Usage: CreateDirectory directory_name
Questions
I do not understand why this error comes up at compile time. When using a variable (especially in this situation where the variable depends on user-input), it does not seem to make sense to complain about the argument when it is not known.
How can I avoid this probolem?
A little puzzle that messes me up is the syntax to refer to variables.The statement MessageBox MB_OK "ini=${IniFile} , config=$config, FOOHOME=$FOOHOME" shows that. I found that I needed to enclose IniFile in {} in order to display its value (I commented out the CreateDir-line to compile the installer and check my assumptions). When do I have to use {}?
If you see any other "unusual" things in my little script, I'd be happy to know ;)
You have a typo, change CreateDirectory "SFOOHOME" to CreateDirectory "$FOOHOME"
You might want to read the documentation again to learn the basics; ${define}, $(langstring) and $variable.

NSIS basics go wrong

So this is a really basic question, but I can't seem to find what I am doing wrong.
So I am fiddling with defines in NSIS and it did not work as I would expect so I have scaled down the problem to its smallest part and I still can not make it work as I would expect.
Script looks as follows:
!ifndef b
!define b ""
!endif
!if $b=="b"
!define a "b"
!else
!define a "c"
!endif
Section
MessageBox MB_OK "a: ${a} b: ${b}"
SectionEnd
I run it with the flag /Db=b.
The output is still:
a: "c" b: "b"
I am missing something trivial here!
b is a define, not a variable:
!ifndef b
!define b ""
!endif
!if "${b}" == "b" # <-- Modify this line.
!define a "b"
!else
!define a "c"
!endif
Section
MessageBox MB_OK "a: ${a} b: ${b}"
SectionEnd
Also, I recommend you quote everything when using if because it'll give an error if define (or value of variable) is empty.

Find a string pattern in a file from an NSIS script

In an NSIS installer script, I'm trying to check if a given httpd.conf file contains the following line :
Include "c:\xxx\yyy.conf"
If so, then my installer script would not append it to the file, otherwise, it would append it.
I've come through {LineFind} but not sure this really makes what i'm trying to achieve.
What could be the simplest way to do a kind of "grep" on a text file from an NSIS script ?
Thank you !
Here is a sample for searching for a given line into a file, using the LogicLib for ease of syntax. The search is stopped as soon as the line is found. This sample works on the sample script itself:
# find.nsi : sample for LineFind from TextFunc.nsh
!include "textfunc.nsh"
!include "logiclib.nsh"
OutFile "find.exe"
!define lookfor `Section` ;will find
;!define lookfor `Sectionn` ;will not find
Var found
Section
StrCpy $found 0
${LineFind} "find.nsi" "/NUL" "1:-1" "GrepFunc"
${if} $found = 1
MessageBox MB_OK "string found"
${else}
MessageBox MB_OK "string NOT found"
${endIf}
SectionEnd
Function GrepFunc
${TrimNewLines} '$R9' $R9
DetailPrint "test for line $R8 `$R9`"
${if} $R9 == "${lookfor}"
StrCpy $found 1 ;set flag
Push "StopLineFind" ;stop find
${else}
Push 0 ;ignore -> continue
${endIf}
FunctionEnd

Move property value into text box using NSIS script

I have created nsi file for my java project.I have created a Textbox and gave as default value.My problem is running the exe file it displays textbox with default value.If the user wants to modify the value in text box that should be Written in property file.I have tried following lines code
InstallOptions::dialog "$PLUGINSDIR\sample.ini"
ReadINIStr $0 "$PLUGINSDIR\sample.ini" "Field 1" State
${ConfigWrite} "$INSTDIR\resource\conf.properties" "AGENT.HOST" "=$0" $R0
But the modified values will be affected in property file.I dont know why this isnot reflected? can anyone help me?
!include LogicLib.nsh
!include TextFunc.nsh
Function pageConfig
InitPluginsDir
# For this example I generate the page directly with the precompiler, you probably want to use a real .ini file.
!tempfile SRCINI
!appendfile "${SRCINI}" "[Settings]$\nNumFields=1$\n"
!appendfile "${SRCINI}" "[Field 1]$\n"
!appendfile "${SRCINI}" "Type=Text$\n"
!appendfile "${SRCINI}" "Left=10$\nRight=100$\nTop=10$\nBottom=24"
File "/oname=$PluginsDir\cfgpage.ini" "${SRCINI}"
!delfile "${SRCINI}"
ClearErrors
${ConfigRead} "$INSTDIR\resource\conf.properties" "AGENT.HOST=" $0
${If} ${Errors}
StrCpy $0 "DefaultHost.com"
${EndIf}
WriteIniStr "$PluginsDir\cfgpage.ini" "Field 1" "State" $0
InstallOptions::dialog "$PluginsDir\cfgpage.ini"
Pop $0
FunctionEnd
Function pageLeaveConfig
ReadINIStr $0 "$PluginsDir\cfgpage.ini" "Field 1" State
${If} $0 == ""
MessageBox mb_iconstop "Please provide a host..."
Abort
${EndIf}
${ConfigWrite} "$INSTDIR\resource\conf.properties" "AGENT.HOST=" "$0" $1
FunctionEnd
Page custom pageConfig pageLeaveConfig

NSIS - Conditionally Display Components

I want the installer/uninstaller to check if any of the possible components are installed, and display only relevan components (installer should display only components not already installed and uninstaller should display only already-installed components)
I'm using MUI.
The component section of my .nsi looks something like this:
; Section descriptions
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${Component1} "Component1 Description"
!insertmacro MUI_DESCRIPTION_TEXT ${Component2} "Component2 Description."
!insertmacro MUI_DESCRIPTION_TEXT ${Component3} "Component3 Description."
!insertmacro MUI_FUNCTION_DESCRIPTION_END
I've tried a couple of different ways:
First, I tried using this macro for checking if a registry exists:
!insertmacro IfKeyExists HKEY_LOCAL_MACHINE SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall "Component1"
Pop $R0
${If} $R0 == 0 #Not installed yet. Display.
!insertmacro MUI_DESCRIPTION_TEXT ${Component1} "Component1"
${EndIf}
This didn't work.
Then I tried using the Registry Plug-In:
${registry::KeyExists} "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Component1" $R0
${If} $R0 == 0 #Not installed yet. Display.
!insertmacro MUI_DESCRIPTION_TEXT ${Component1} "Component1"
${EndIf}
This didn't work either.
So then I thought maybe there was some other more basic problem, and I tried this:
StrCpy $0 "0"
${If} $0 == "1"
!insertmacro MUI_DESCRIPTION_TEXT ${Component1} "Component1 Description."
${EndIf}
But even then I still saw "Component1" as a possible component during installation!
What am I doing wrong and how can I go about achieving my goal?
Thanks in advance!
To select a section (= a component) at runtime you can use SectionGetFlag / SectionSetFlag and some bit manipulation :
SectionGetFlags ${test_section_id} $0
IntOp $0 $0 | ${SF_SELECTED}
SectionSetFlags ${test_section_id} $0
To deselect it, just toogle its selected bit, with
SectionGetFlags ${test_section_id} $0
IntOp $0 $0 ^ ${SF_SELECTED}
SectionSetFlags ${test_section_id} $0
These are simpler if you include the Sections.nsh header and use the SelectSection and UnselectSection macros.
If you want to hide a section, you just need to set its text to "" (empty string)
SectionSetText ${test_section_id} ""
You are modifying the wrong text. MUI_DESCRIPTION_TEXT is the long description that is typically displayed when hovering the mouse over a component. But the visibility of a section depends on its name to be empty or not empty.
Therefore you have to use this instead to hide your component1:
SectionSetText ${Component1} ""

Resources