Merge branch 'master' into concurrent_processing

# Conflicts:
#	Duplicati/Library/Main/Strings.cs
This commit is contained in:
Kenneth Skovhede
2016-10-09 01:29:24 +02:00
193 changed files with 26419 additions and 27305 deletions

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

+36
View File
@@ -0,0 +1,36 @@
@echo off
rem ImageMagick and inkscape needed
mkdir temp
inkscape -z -e temp/application_16.png -w 16 -h 16 application_icon.svg
inkscape -z -e temp/application_20.png -w 20 -h 20 application_icon.svg
inkscape -z -e temp/application_24.png -w 24 -h 24 application_icon.svg
inkscape -z -e temp/application_32.png -w 32 -h 32 application_icon.svg
inkscape -z -e temp/application_40.png -w 40 -h 40 application_icon.svg
inkscape -z -e temp/application_48.png -w 48 -h 48 application_icon.svg
inkscape -z -e temp/application_64.png -w 64 -h 64 application_icon.svg
inkscape -z -e temp/application_96.png -w 96 -h 96 application_icon.svg
inkscape -z -e temp/application_256.png -w 256 -h 256 application_icon.svg
mkdir osx_appicon
inkscape -z -e osx_appicon/icon_16x16.png -w 16 -h 16 application_icon.svg
inkscape -z -e osx_appicon/icon_16x16@2x.png -w 32 -h 32 application_icon.svg
inkscape -z -e osx_appicon/icon_32x32.png -w 32 -h 32 application_icon.svg
inkscape -z -e osx_appicon/icon_32x32@2x.png -w 64 -h 64 application_icon.svg
inkscape -z -e osx_appicon/icon_64x64.png -w 64 -h 64 application_icon.svg
inkscape -z -e osx_appicon/icon_64x64@2x.png -w 128 -h 128 application_icon.svg
inkscape -z -e osx_appicon/icon_128x128.png -w 128 -h 128 application_icon.svg
inkscape -z -e osx_appicon/icon_128x128@2x.png -w 256 -h 256 application_icon.svg
inkscape -z -e osx_appicon/icon_256x256.png -w 256 -h 256 application_icon.svg
inkscape -z -e osx_appicon/icon_256x256@2x.png -w 512 -h 512 application_icon.svg
inkscape -z -e osx_appicon/icon_512x512.png -w 512 -h 512 application_icon.svg
inkscape -z -e osx_appicon/icon_512x512@2x.png -w 1024 -h 1024 application_icon.svg
rem On OSX, the icns is created by:
rem mv osx_appicon osx_appicon.iconset
rem iconutil -c icns osx_appicon.iconset
convert temp/application_16.png temp/application_20.png temp/application_24.png temp/application_32.png temp/application_40.png temp/application_48.png temp/application_64.png temp/application_96.png temp/application_256.png application_icon.ico
rd /s /q temp
pause
-65
View File
@@ -1,65 +0,0 @@
@echo off
rem ImageMagick and inkscape needed
rem On non-windows, replace \ with /
mkdir temp
inkscape -z -e temp\application_16.png -w 16 -h 16 application_icon.svg
inkscape -z -e temp\application_20.png -w 20 -h 20 application_icon.svg
inkscape -z -e temp\application_24.png -w 24 -h 24 application_icon.svg
inkscape -z -e temp\application_32.png -w 32 -h 32 application_icon.svg
inkscape -z -e temp\application_40.png -w 40 -h 40 application_icon.svg
inkscape -z -e temp\application_48.png -w 48 -h 48 application_icon.svg
inkscape -z -e temp\application_64.png -w 64 -h 64 application_icon.svg
inkscape -z -e temp\application_96.png -w 96 -h 96 application_icon.svg
inkscape -z -e temp\application_256.png -w 256 -h 256 application_icon.svg
inkscape -z -e temp\error_16.png -w 16 -h 16 tray_icons_error.svg
inkscape -z -e temp\error_24.png -w 24 -h 24 tray_icons_error.svg
inkscape -z -e temp\error_32.png -w 32 -h 32 tray_icons_error.svg
inkscape -z -e temp\error_48.png -w 48 -h 48 tray_icons_error.svg
inkscape -z -e temp\error_256.png -w 256 -h 256 tray_icons_error.svg
inkscape -z -e temp\inactive_16.png -w 16 -h 16 tray_icons_inactive.svg
inkscape -z -e temp\inactive_24.png -w 24 -h 24 tray_icons_inactive.svg
inkscape -z -e temp\inactive_32.png -w 32 -h 32 tray_icons_inactive.svg
inkscape -z -e temp\inactive_48.png -w 48 -h 48 tray_icons_inactive.svg
inkscape -z -e temp\inactive_256.png -w 256 -h 256 tray_icons_inactive.svg
inkscape -z -e temp\paused_16.png -w 16 -h 16 tray_icons_paused.svg
inkscape -z -e temp\paused_24.png -w 24 -h 24 tray_icons_paused.svg
inkscape -z -e temp\paused_32.png -w 32 -h 32 tray_icons_paused.svg
inkscape -z -e temp\paused_48.png -w 48 -h 48 tray_icons_paused.svg
inkscape -z -e temp\paused_256.png -w 256 -h 256 tray_icons_paused.svg
inkscape -z -e temp\running_16.png -w 16 -h 16 tray_icons_running.svg
inkscape -z -e temp\running_24.png -w 24 -h 24 tray_icons_running.svg
inkscape -z -e temp\running_32.png -w 32 -h 32 tray_icons_running.svg
inkscape -z -e temp\running_48.png -w 48 -h 48 tray_icons_running.svg
inkscape -z -e temp\running_256.png -w 256 -h 256 tray_icons_running.svg
mkdir osx_appicon
inkscape -z -e osx_appicon\icon_16x16.png -w 16 -h 16 application_icon.svg
inkscape -z -e osx_appicon\icon_16x16@2x.png -w 32 -h 32 application_icon.svg
inkscape -z -e osx_appicon\icon_32x32.png -w 32 -h 32 application_icon.svg
inkscape -z -e osx_appicon\icon_32x32@2x.png -w 64 -h 64 application_icon.svg
inkscape -z -e osx_appicon\icon_64x64.png -w 64 -h 64 application_icon.svg
inkscape -z -e osx_appicon\icon_64x64@2x.png -w 128 -h 128 application_icon.svg
inkscape -z -e osx_appicon\icon_128x128.png -w 128 -h 128 application_icon.svg
inkscape -z -e osx_appicon\icon_128x128@2x.png -w 256 -h 256 application_icon.svg
inkscape -z -e osx_appicon\icon_256x256.png -w 256 -h 256 application_icon.svg
inkscape -z -e osx_appicon\icon_256x256@2x.png -w 512 -h 512 application_icon.svg
inkscape -z -e osx_appicon\icon_512x512.png -w 512 -h 512 application_icon.svg
inkscape -z -e osx_appicon\icon_512x512@2x.png -w 1024 -h 1024 application_icon.svg
rem On OSX, the icns is created by:
rem mv osx_appicon osx_appicon.iconset
rem iconutil -c icns osx_appicon.iconset
convert temp\application_16.png temp\application_20.png temp\application_24.png temp\application_32.png temp\application_40.png temp\application_48.png temp\application_64.png temp\application_96.png temp\application_256.png application_icon.ico
convert temp\error_16.png temp\error_24.png temp\error_32.png temp\error_48.png temp\error_256.png error.ico
convert temp\inactive_16.png temp\inactive_24.png temp\inactive_32.png temp\inactive_48.png temp\inactive_256.png inactive.ico
convert temp\paused_16.png temp\paused_24.png temp\paused_32.png temp\paused_48.png temp\paused_256.png paused.ico
convert temp\running_16.png temp\running_24.png temp\running_32.png temp\running_48.png temp\running_256.png running.ico
rd /s /q temp
@@ -0,0 +1,35 @@
@echo off
rem ImageMagick and inkscape needed
mkdir temp
inkscape -z -e temp/pause_16.png -w 16 -h 16 tray_context_menu_pause.svg
inkscape -z -e temp/pause_24.png -w 24 -h 24 tray_context_menu_pause.svg
inkscape -z -e temp/pause_32.png -w 32 -h 32 tray_context_menu_pause.svg
inkscape -z -e temp/pause_48.png -w 48 -h 48 tray_context_menu_pause.svg
inkscape -z -e temp/pause_256.png -w 256 -h 256 tray_context_menu_pause.svg
inkscape -z -e temp/resume_16.png -w 16 -h 16 tray_context_menu_resume.svg
inkscape -z -e temp/resume_24.png -w 24 -h 24 tray_context_menu_resume.svg
inkscape -z -e temp/resume_32.png -w 32 -h 32 tray_context_menu_resume.svg
inkscape -z -e temp/resume_48.png -w 48 -h 48 tray_context_menu_resume.svg
inkscape -z -e temp/resume_256.png -w 256 -h 256 tray_context_menu_resume.svg
inkscape -z -e temp/open_16.png -w 16 -h 16 tray_context_menu_open.svg
inkscape -z -e temp/open_24.png -w 24 -h 24 tray_context_menu_open.svg
inkscape -z -e temp/open_32.png -w 32 -h 32 tray_context_menu_open.svg
inkscape -z -e temp/open_48.png -w 48 -h 48 tray_context_menu_open.svg
inkscape -z -e temp/open_256.png -w 256 -h 256 tray_context_menu_open.svg
inkscape -z -e temp/quit_16.png -w 16 -h 16 tray_context_menu_quit.svg
inkscape -z -e temp/quit_24.png -w 24 -h 24 tray_context_menu_quit.svg
inkscape -z -e temp/quit_32.png -w 32 -h 32 tray_context_menu_quit.svg
inkscape -z -e temp/quit_48.png -w 48 -h 48 tray_context_menu_quit.svg
inkscape -z -e temp/quit_256.png -w 256 -h 256 tray_context_menu_quit.svg
convert temp/pause_16.png temp/pause_24.png temp/pause_32.png temp/pause_48.png temp/pause_256.png context_menu_pause.ico
convert temp/resume_16.png temp/resume_24.png temp/resume_32.png temp/resume_48.png temp/resume_256.png context_menu_play.ico
convert temp/open_16.png temp/open_24.png temp/open_32.png temp/open_48.png temp/open_256.png context_menu_open.ico
convert temp/quit_16.png temp/quit_24.png temp/quit_32.png temp/quit_48.png temp/quit_256.png context_menu_quit.ico
rd /s /q temp
pause
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,13 @@
<svg id="tray_menu_icons" data-name="tray menu icons" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<defs>
<style>
.cls-1 {
fill: #65b1dd;
}
</style>
</defs>
<title>tray context menu icons</title>
<g id="show_Kopie" data-name="show Kopie">
<polygon class="cls-1" points="8 4.001 2 10.301 3.5 11.876 8 7.072 12.5 11.876 14 10.301 8 4.001"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 427 B

@@ -0,0 +1,14 @@
<svg id="tray_menu_icons" data-name="tray menu icons" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<defs>
<style>
.cls-1 {
fill: #65b1dd;
}
</style>
</defs>
<title>tray context menu icons</title>
<g id="pause_Kopie" data-name="pause Kopie">
<rect class="cls-1" x="4" y="2" width="3" height="12"/>
<rect class="cls-1" x="9" y="2" width="3" height="12"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 445 B

@@ -0,0 +1,13 @@
<svg id="tray_menu_icons" data-name="tray menu icons" xmlns="http://www.w3.org/2000/svg" width="15.997" height="16" viewBox="0 0 15.997 16">
<defs>
<style>
.cls-1 {
fill: #65b1dd;
}
</style>
</defs>
<title>tray context menu icons</title>
<g id="stop_Kopie" data-name="stop Kopie">
<polygon id="stop" class="cls-1" points="13.998 3.95 12.048 2 7.998 6.05 3.948 2 1.998 3.95 6.049 8 1.998 12.05 3.948 14 7.998 9.95 12.048 14 13.998 12.05 9.949 8 13.998 3.95"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 513 B

@@ -0,0 +1,13 @@
<svg id="tray_menu_icons" data-name="tray menu icons" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<defs>
<style>
.cls-1 {
fill: #65b1dd;
}
</style>
</defs>
<title>tray context menu icons</title>
<g id="resume_Kopie" data-name="resume Kopie">
<polygon class="cls-1" points="5 14 13 8 5 2 5 14"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 384 B

+35
View File
@@ -0,0 +1,35 @@
@echo off
rem ImageMagick and inkscape needed
mkdir temp
inkscape -z -e temp/error_16.png -w 16 -h 16 tray_icons_error.svg
inkscape -z -e temp/error_24.png -w 24 -h 24 tray_icons_error.svg
inkscape -z -e temp/error_32.png -w 32 -h 32 tray_icons_error.svg
inkscape -z -e temp/error_48.png -w 48 -h 48 tray_icons_error.svg
inkscape -z -e temp/error_256.png -w 256 -h 256 tray_icons_error.svg
inkscape -z -e temp/inactive_16.png -w 16 -h 16 tray_icons_inactive.svg
inkscape -z -e temp/inactive_24.png -w 24 -h 24 tray_icons_inactive.svg
inkscape -z -e temp/inactive_32.png -w 32 -h 32 tray_icons_inactive.svg
inkscape -z -e temp/inactive_48.png -w 48 -h 48 tray_icons_inactive.svg
inkscape -z -e temp/inactive_256.png -w 256 -h 256 tray_icons_inactive.svg
inkscape -z -e temp/paused_16.png -w 16 -h 16 tray_icons_paused.svg
inkscape -z -e temp/paused_24.png -w 24 -h 24 tray_icons_paused.svg
inkscape -z -e temp/paused_32.png -w 32 -h 32 tray_icons_paused.svg
inkscape -z -e temp/paused_48.png -w 48 -h 48 tray_icons_paused.svg
inkscape -z -e temp/paused_256.png -w 256 -h 256 tray_icons_paused.svg
inkscape -z -e temp/running_16.png -w 16 -h 16 tray_icons_running.svg
inkscape -z -e temp/running_24.png -w 24 -h 24 tray_icons_running.svg
inkscape -z -e temp/running_32.png -w 32 -h 32 tray_icons_running.svg
inkscape -z -e temp/running_48.png -w 48 -h 48 tray_icons_running.svg
inkscape -z -e temp/running_256.png -w 256 -h 256 tray_icons_running.svg
convert temp/error_16.png temp/error_24.png temp/error_32.png temp/error_48.png temp/error_256.png error.ico
convert temp/inactive_16.png temp/inactive_24.png temp/inactive_32.png temp/inactive_48.png temp/inactive_256.png inactive.ico
convert temp/paused_16.png temp/paused_24.png temp/paused_32.png temp/paused_48.png temp/paused_256.png paused.ico
convert temp/running_16.png temp/running_24.png temp/running_32.png temp/running_48.png temp/running_256.png running.ico
rd /s /q temp
pause

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before

Width:  |  Height:  |  Size: 451 B

After

Width:  |  Height:  |  Size: 451 B

Before

Width:  |  Height:  |  Size: 372 B

After

Width:  |  Height:  |  Size: 372 B

Before

Width:  |  Height:  |  Size: 378 B

After

Width:  |  Height:  |  Size: 378 B

Before

Width:  |  Height:  |  Size: 418 B

After

Width:  |  Height:  |  Size: 418 B

Before

Width:  |  Height:  |  Size: 778 B

After

Width:  |  Height:  |  Size: 778 B

Before

Width:  |  Height:  |  Size: 626 B

After

Width:  |  Height:  |  Size: 626 B

Before

Width:  |  Height:  |  Size: 631 B

After

Width:  |  Height:  |  Size: 631 B

Before

Width:  |  Height:  |  Size: 690 B

After

Width:  |  Height:  |  Size: 690 B

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

@@ -1,26 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LocalizationTool", "LocalizationTool\LocalizationTool.csproj", "{F093C3D3-28A8-405E-96C0-97AE2C296CCC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Utility", "..\..\Duplicati\Library\Utility\Duplicati.Library.Utility.csproj", "{DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F093C3D3-28A8-405E-96C0-97AE2C296CCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F093C3D3-28A8-405E-96C0-97AE2C296CCC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F093C3D3-28A8-405E-96C0-97AE2C296CCC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F093C3D3-28A8-405E-96C0-97AE2C296CCC}.Release|Any CPU.Build.0 = Release|Any CPU
{DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
@@ -1,122 +0,0 @@
#region Disclaimer / License
// Copyright (C) 2015, The Duplicati Team
// http://www.duplicati.com, info@duplicati.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LocalizationTool
{
public class CSVReader : IDisposable
{
private System.IO.StreamReader m_reader;
public CSVReader(string filename)
{
m_reader = new System.IO.StreamReader(filename, System.Text.Encoding.UTF8, true);
}
public List<string> AdvanceLine()
{
bool inQuote = false;
StringBuilder sb = new StringBuilder();
do
{
string line = m_reader.ReadLine();
if (line == null)
{
if (sb.Length == 0)
return null;
else
throw new Exception("Unexpected EOF, buffer was: " + sb.ToString());
}
sb.AppendLine(line);
foreach (char c in line)
if (c == '"')
inQuote = !inQuote;
} while (inQuote);
string txt = sb.ToString();
if (txt.EndsWith("\r\n"))
txt = txt.Substring(0, txt.Length - 2);
else if (txt.EndsWith("\r\n"))
txt = txt.Substring(0, txt.Length - 2);
else if (txt.EndsWith("\n"))
txt = txt.Substring(0, txt.Length - 1);
else if (txt.EndsWith("\r"))
txt = txt.Substring(0, txt.Length - 1);
inQuote = false;
int ix = 0;
List<string> result = new List<string>();
for(int i = 0; i < txt.Length; i++)
if (inQuote)
{
if (txt[i] == '"')
inQuote = false;
}
else
{
if (txt[i] == '"')
inQuote = true;
else if (txt[i] == ',')
{
result.Add(CleanString(txt.Substring(ix, i - ix)));
ix = i + 1;
}
}
if (inQuote)
throw new Exception("Failed to parse line: " + txt);
if (ix != txt.Length)
result.Add(CleanString(txt.Substring(ix)));
return result;
}
private string CleanString(string v)
{
//v = v.Trim();
if (v.StartsWith("\"") && v.EndsWith("\""))
return v.Substring(1, v.Length - 2).Replace("\"\"", "\"");
else
return v;
}
#region IDisposable Members
public void Dispose()
{
if (m_reader != null)
{
m_reader.Dispose();
m_reader = null;
}
}
#endregion
}
}
@@ -1,239 +0,0 @@
#region Disclaimer / License
// Copyright (C) 2015, The Duplicati Team
// http://www.duplicati.com, info@duplicati.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LocalizationTool
{
public static class LinqHelpers
{
//
// Summary:
// Creates a System.Collections.Generic.Dictionary<TKey,TValue> from an System.Collections.Generic.IEnumerable<T>
// according to a specified key selector function.
//
// Parameters:
// source:
// An System.Collections.Generic.IEnumerable<T> to create a System.Collections.Generic.Dictionary<TKey,TValue>
// from.
//
// keySelector:
// A function to extract a key from each element.
//
// Type parameters:
// TSource:
// The type of the elements of source.
//
// TKey:
// The type of the key returned by keySelector.
//
// Returns:
// A System.Collections.Generic.Dictionary<TKey,TValue> that contains keys and
// values.
//
// Exceptions:
// System.ArgumentNullException:
// source or keySelector is null. -or- keySelector produces a key that is null.
public static Dictionary<TKey, TSource> ToSafeDictionary<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, string locationName)
{
if (source == null)
throw new ArgumentNullException("source");
if (keySelector == null)
throw new ArgumentNullException("keySelector");
Dictionary<TKey, TSource> res = new Dictionary<TKey, TSource>();
foreach (TSource el in source)
{
TKey k = keySelector(el);
if (res.ContainsKey(k))
Console.WriteLine("*** Warning, duplicate key: " + k.ToString() + " in " + locationName);
else
res.Add(k, el);
}
return res;
}
//
// Summary:
// Creates a System.Collections.Generic.Dictionary<TKey,TValue> from an System.Collections.Generic.IEnumerable<T>
// according to specified key selector and element selector functions.
//
// Parameters:
// source:
// An System.Collections.Generic.IEnumerable<T> to create a System.Collections.Generic.Dictionary<TKey,TValue>
// from.
//
// keySelector:
// A function to extract a key from each element.
//
// elementSelector:
// A transform function to produce a result element value from each element.
//
// Type parameters:
// TSource:
// The type of the elements of source.
//
// TKey:
// The type of the key returned by keySelector.
//
// TElement:
// The type of the value returned by elementSelector.
//
// Returns:
// A System.Collections.Generic.Dictionary<TKey,TValue> that contains values
// of type TElement selected from the input sequence.
//
// Exceptions:
// System.ArgumentNullException:
// source or keySelector or elementSelector is null. -or- keySelector produces
// a key that is null.
public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, string locationName)
{
if (source == null)
throw new ArgumentNullException("source");
if (keySelector == null)
throw new ArgumentNullException("keySelector");
if (elementSelector == null)
throw new ArgumentNullException("elementSelector");
Dictionary<TKey, TElement> res = new Dictionary<TKey, TElement>();
foreach (TSource el in source)
{
TKey k = keySelector(el);
if (res.ContainsKey(k))
Console.WriteLine("*** Warning, duplicate key: " + k.ToString() + " in " + locationName);
else
res.Add(k, elementSelector(el));
}
return res;
}
//
// Summary:
// Creates a System.Collections.Generic.Dictionary<TKey,TValue> from an System.Collections.Generic.IEnumerable<T>
// according to a specified key selector function and key comparer.
//
// Parameters:
// source:
// An System.Collections.Generic.IEnumerable<T> to create a System.Collections.Generic.Dictionary<TKey,TValue>
// from.
//
// keySelector:
// A function to extract a key from each element.
//
// comparer:
// An System.Collections.Generic.IEqualityComparer<T> to compare keys.
//
// Type parameters:
// TSource:
// The type of the elements of source.
//
// TKey:
// The type of the keys returned by keySelector.
//
// Returns:
// A System.Collections.Generic.Dictionary<TKey,TValue> that contains keys and
// values.
//
// Exceptions:
// System.ArgumentNullException:
// source or keySelector is null. -or- keySelector produces a key that is null.
public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer, string locationName)
{
if (source == null)
throw new ArgumentNullException("source");
if (keySelector == null)
throw new ArgumentNullException("keySelector");
Dictionary<TKey, TSource> res = new Dictionary<TKey, TSource>(comparer);
foreach (TSource el in source)
{
TKey k = keySelector(el);
if (res.ContainsKey(k))
Console.WriteLine("*** Warning, duplicate key: " + k.ToString() + " in " + locationName);
else
res.Add(k, el);
}
return res;
}
//
// Summary:
// Creates a System.Collections.Generic.Dictionary<TKey,TValue> from an System.Collections.Generic.IEnumerable<T>
// according to a specified key selector function, a comparer, and an element
// selector function.
//
// Parameters:
// source:
// An System.Collections.Generic.IEnumerable<T> to create a System.Collections.Generic.Dictionary<TKey,TValue>
// from.
//
// keySelector:
// A function to extract a key from each element.
//
// elementSelector:
// A transform function to produce a result element value from each element.
//
// comparer:
// An System.Collections.Generic.IEqualityComparer<T> to compare keys.
//
// Type parameters:
// TSource:
// The type of the elements of source.
//
// TKey:
// The type of the key returned by keySelector.
//
// TElement:
// The type of the value returned by elementSelector.
//
// Returns:
// A System.Collections.Generic.Dictionary<TKey,TValue> that contains values
// of type TElement selected from the input sequence.
//
// Exceptions:
// System.ArgumentNullException:
// source or keySelector or elementSelector is null. -or- keySelector produces
// a key that is null.
public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer, string locationName)
{
if (source == null)
throw new ArgumentNullException("source");
if (keySelector == null)
throw new ArgumentNullException("keySelector");
if (elementSelector == null)
throw new ArgumentNullException("elementSelector");
Dictionary<TKey, TElement> res = new Dictionary<TKey, TElement>(comparer);
foreach (TSource el in source)
{
TKey k = keySelector(el);
if (res.ContainsKey(k))
Console.WriteLine("*** Warning, duplicate key: " + k.ToString() + " in " + locationName);
else
res.Add(k, elementSelector(el));
}
return res;
}
}
}
@@ -1,155 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{F093C3D3-28A8-405E-96C0-97AE2C296CCC}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>LocalizationTool</RootNamespace>
<AssemblyName>LocalizationTool</AssemblyName>
<FileUpgradeFlags>
</FileUpgradeFlags>
<OldToolsVersion>3.5</OldToolsVersion>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\..\Duplicati\Localization\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Commandlineparameters>export</Commandlineparameters>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\..\Duplicati\Localization\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Compile Include="CSVReader.cs" />
<Compile Include="LinqHelpers.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="UpdateGUI.resx">
<DependentUpon>UpdateGUI.cs</DependentUpon>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<Compile Include="ResXCompiler.cs" />
<Compile Include="UpdateGUI.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="UpdateGUI.Designer.cs">
<DependentUpon>UpdateGUI.cs</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="Resources\Empty_resx.txt" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Duplicati\Library\Utility\Duplicati.Library.Utility.csproj">
<Project>{DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}</Project>
<Name>Duplicati.Library.Utility</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.2.0">
<Visible>False</Visible>
<ProductName>.NET Framework 2.0 %28x86%29</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.0">
<Visible>False</Visible>
<ProductName>.NET Framework 3.0 %28x86%29</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
<Visible>False</Visible>
<ProductName>Windows Installer 3.1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\Batchjob.bat.txt" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
@@ -1,999 +0,0 @@
#region Disclaimer / License
// Copyright (C) 2015, The Duplicati Team
// http://www.duplicati.com, info@duplicati.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion
using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.IO;
using System.Net;
namespace LocalizationTool
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
if (args.Length == 0)
{
PrintUsage();
return;
}
//All relative paths are to the app dir
System.IO.Directory.SetCurrentDirectory(Application.StartupPath);
string loc = args.Length >= 2 ? args[1] : null;
switch (args[0].ToLower())
{
case "clean":
case "cleanup":
Clean(loc);
break;
case "build":
Compile(loc);
break;
case "update":
Update(loc);
break;
case "create":
if (args.Length != 2)
{
Console.WriteLine("missing locale identifier");
PrintUsage();
return;
}
Create(loc);
break;
case "report":
Report(loc);
break;
case "export":
Export(loc);
break;
case "exportdiff":
if (args.Length != 3)
{
Console.WriteLine("missing locale identifier or input CSV file");
PrintUsage();
return;
}
ExportDiff(loc, args[2]);
break;
case "import":
if (args.Length != 3)
{
Console.WriteLine("missing locale identifier or input CSV file");
PrintUsage();
return;
}
Import(loc, args[2]);
break;
case "guiupdate":
Application.EnableVisualStyles();
Application.DoEvents();
Report(loc);
new UpdateGUI(System.IO.Path.Combine(Application.StartupPath, "report." + loc + ".xml")).ShowDialog();
break;
case "webpull":
WebPull(loc, false);
break;
case "webupdate":
WebPull(loc, true);
break;
default:
PrintUsage();
return;
}
}
private static void PrintUsage()
{
Console.WriteLine("Usage: ");
Console.WriteLine("LocalizationTool.exe CLEAN [locale identifier]");
Console.WriteLine("LocalizationTool.exe BUILD [locale identifier]");
Console.WriteLine("LocalizationTool.exe UPDATE [locale identifier]");
Console.WriteLine("LocalizationTool.exe REPORT [locale identifier]");
Console.WriteLine("LocalizationTool.exe CREATE <locale indentifier>");
Console.WriteLine("LocalizationTool.exe GUIUPDATE <locale identifier>");
Console.WriteLine("LocalizationTool.exe EXPORT [locale identifier]");
Console.WriteLine("LocalizationTool.exe IMPORT <locale identifier> <input CSV file>");
Console.WriteLine("LocalizationTool.exe EXPORTDIFF <locale identifier> <input CSV file>");
Console.WriteLine("LocalizationTool.exe WEBPULL [locale identifier]");
Console.WriteLine("LocalizationTool.exe WEBUPDATE [locale identifier]");
}
private struct CSVEntry
{
public string Filename;
public string Fieldkey;
public string Origvalue;
public string Value;
public string Status;
public string[] extraFields;
public CSVEntry(List<string> fields)
{
Filename = fields[0];
Fieldkey = fields[1];
Status = fields[2];
Origvalue = fields[3];
Value = fields[4];
if (fields.Count > 5)
{
extraFields = new string[fields.Count - 5];
fields.CopyTo(5, extraFields, 0, extraFields.Length);
}
else
extraFields = null;
}
}
private static bool IsValidCulture(string c)
{
try
{
System.Globalization.CultureInfo.GetCultureInfo(c);
return true;
}
catch
{
return false;
}
}
/// <summary>
/// Downloads updated CSV files from the web and generates diff files, and optionally updates the local CSV files
/// </summary>
/// <param name="loc">The culture to use</param>
/// <param name="update">True if the elements should be updated as well</param>
private static void WebPull(string loc, bool update)
{
var locs =
from n in Directory.GetFiles(Application.StartupPath, "report.*.url")
where IsValidCulture(Path.GetFileNameWithoutExtension(n).Substring("report.".Length))
select Path.GetFileNameWithoutExtension(n).Substring("report.".Length);
if (!string.IsNullOrEmpty(loc))
{
locs = locs.Where(c => string.Equals(c, loc, StringComparison.CurrentCultureIgnoreCase));
if (locs.Count() == 0)
{
Console.WriteLine("No URL file found for culture {0}", loc);
return;
}
}
if (locs.Count() == 0)
{
Console.WriteLine("No culture.url files found");
return;
}
foreach (var culture in locs)
{
var url = string.Format("report.{0}.url", culture);
var file = string.Format("report.{0}.new.csv", culture);
if (File.Exists(file))
File.Delete(file);
Console.Write("Downloading updated CSV for {0} ... ", culture);
try { new WebClient().DownloadFile(File.ReadAllText(url).Trim(), file); }
catch (Exception ex)
{
Console.WriteLine("Failed!");
Console.WriteLine(" -> {0}", ex.Message);
continue;
}
Console.WriteLine("Done!");
if (update)
{
Console.Write("Updating local files for {0} .", culture);
ExportDiff(culture, file);
Console.Write(".");
Import(culture, file);
Console.Write(".");
Export(culture);
Console.WriteLine(" Done!");
}
}
}
/// <summary>
/// Imports a CSV file into a dictionary format,
/// Outer key is filename, inner key is fieldname, inner value is translated text
/// </summary>
/// <param name="file">The CSV file to read from</param>
/// <param name="loc">The current culture string</param>
/// <returns>Dictionary where outer key is filename, inner key is fieldname, inner value is translated text</returns>
private static Dictionary<string, Dictionary<string, CSVEntry>> ImportCSV(string file, string loc, Func<CSVEntry, bool> filter)
{
Dictionary<string, Dictionary<string, CSVEntry>> values = new Dictionary<string, Dictionary<string, CSVEntry>>();
using (CSVReader r = new CSVReader(file))
{
List<string> fields = null;
while ((fields = r.AdvanceLine()) != null)
if (fields.Count >= 5)
{
CSVEntry e = new CSVEntry(fields);
e.Filename = System.IO.Path.Combine(System.IO.Path.Combine(Application.StartupPath, loc), e.Filename);
if (filter != null && filter(e))
continue;
if (!values.ContainsKey(e.Filename))
values.Add(e.Filename, new Dictionary<string, CSVEntry>());
Dictionary<string, CSVEntry> l = values[e.Filename];
l[e.Fieldkey] = e;
}
}
return values;
}
private static void Import(string loc, string p)
{
//Outer key is filename, inner key is fieldname, inner value is translated text
Dictionary<string, Dictionary<string, CSVEntry>> values = ImportCSV(p, loc, e => e.Value.Trim().Length == 0 || e.Value == e.Origvalue);
if (Duplicati.Library.Utility.Utility.IsClientLinux)
{
Dictionary<string, Dictionary<string, CSVEntry>> remap = new Dictionary<string, Dictionary<string, CSVEntry>>();
foreach(string key in values.Keys)
remap[key.Replace("\\", System.IO.Path.DirectorySeparatorChar.ToString ())] = values[key];
values = remap;
}
string folder = System.IO.Path.Combine(Application.StartupPath, loc);
if (!System.IO.Directory.Exists(folder))
Create(loc);
else
Update(loc);
XNamespace xmlns = "";
foreach (ResXFileInfo inf in GetResXList(loc))
{
if (System.IO.File.Exists(inf.TargetFile) && values.ContainsKey(inf.TargetFile))
{
XDocument targetDoc = XDocument.Load(inf.TargetFile);
XNode insertTarget = targetDoc.Element("root").LastNode;
var targetVals = targetDoc.Element("root").Elements("data").ToSafeDictionary(c => c.Attribute("name").Value, inf.TargetFile);
var sourceVals = values[inf.TargetFile];
values.Remove(inf.TargetFile);
bool updated = false;
foreach (var item in sourceVals)
if (targetVals.ContainsKey(item.Key))
{
if (targetVals[item.Key].Element("value").Value.Replace("\r\n", "\n") != item.Value.Value.Replace("\r\n", "\n"))
{
updated = true;
targetVals[item.Key].Element("value").Value = item.Value.Value;
}
}
else
{
updated = true;
insertTarget.AddAfterSelf(new XElement("data",
new XAttribute("name", item.Key),
new XAttribute(xmlns + "space", "preserve"),
new XElement("value", item.Value.Value)
));
}
if (updated)
targetDoc.Save(inf.TargetFile);
}
}
if (values.Count != 0)
Console.WriteLine("The following files were translated but did not exist: " + string.Join(Environment.NewLine, values.Keys.ToArray()));
}
public static IEnumerable<string> GetLocaleFolders(string locid)
{
if (string.IsNullOrEmpty(locid))
return System.IO.Directory.GetDirectories(Application.StartupPath).Where(c =>
{
try
{
System.Globalization.CultureInfo.GetCultureInfo(System.IO.Path.GetFileName(c));
return true;
}
catch
{
return false;
}
}).Select(c => System.IO.Path.GetFileName(c));
else
return new string[] { locid };
}
private static void Export(string cultures)
{
Update(cultures);
Report(cultures);
foreach (string culture in GetLocaleFolders(cultures))
{
XDocument doc = XDocument.Load(System.IO.Path.Combine(Application.StartupPath, "report." + culture + ".xml"));
string outfile = System.IO.Path.Combine(Application.StartupPath, "report." + culture + ".csv");
Dictionary<string, Dictionary<string, CSVEntry>> elems = new Dictionary<string, Dictionary<string, CSVEntry>>();
foreach (var file in doc.Element("root").Elements("file"))
{
string filename = file.Attribute("filename").Value.Substring(Duplicati.Library.Utility.Utility.AppendDirSeparator(Application.StartupPath).Length);
filename = filename.Substring(culture.Length + 1);
Dictionary<string, CSVEntry> e = new Dictionary<string, CSVEntry>();
elems[filename] = e;
foreach (var item in file.Element("updated").Elements("item"))
{
CSVEntry c = new CSVEntry();
c.Filename = filename;
c.Fieldkey = item.Attribute("name").Value;
c.Status = item.Parent.Name.LocalName;
c.Origvalue = item.Element("original").Value;
c.Value = item.Element("translated").Value;
e.Add(c.Fieldkey, c);
}
foreach (var item in file.Element("missing").Elements("item"))
{
CSVEntry c = new CSVEntry();
c.Filename = filename;
c.Fieldkey = item.Attribute("name").Value;
c.Status = item.Parent.Name.LocalName;
c.Origvalue = item.Value;
c.Value = "";
e.Add(c.Fieldkey, c);
}
foreach (var item in file.Element("not-updated").Elements("item"))
{
CSVEntry c = new CSVEntry();
c.Filename = filename;
c.Fieldkey = item.Attribute("name").Value;
c.Status = item.Parent.Name.LocalName;
c.Origvalue = item.Value;
c.Value = "";
e.Add(c.Fieldkey, c);
}
foreach (var item in file.Element("unused").Elements("item"))
{
CSVEntry c = new CSVEntry();
c.Filename = filename;
c.Fieldkey = item.Attribute("name").Value;
c.Status = item.Parent.Name.LocalName;
c.Origvalue = "";
c.Value = item.Value;
e.Add(c.Fieldkey, c);
}
}
WriteCSVFile(outfile, culture, elems, null);
}
}
private static void ExportDiff(string culture, string inputfile)
{
inputfile = System.IO.Path.GetFullPath(inputfile);
string currentFile = System.IO.Path.Combine(Application.StartupPath, "report." + culture + ".csv");
string diffFile = System.IO.Path.Combine(Application.StartupPath, "report." + culture + ".diff.csv");
if (currentFile.Equals(inputfile, Duplicati.Library.Utility.Utility.ClientFilenameStringComparision))
throw new Exception("Input file will be overwritten, please use another file");
Export(culture);
//Outer key is filename, inner key is fieldname, inner value is translated text
Dictionary<string, Dictionary<string, CSVEntry>> inputValues = ImportCSV(inputfile, culture, null);
Dictionary<string, Dictionary<string, CSVEntry>> currentValues = ImportCSV(currentFile, culture, null);
Dictionary<string, Dictionary<string, CSVEntry>> added = new Dictionary<string, Dictionary<string, CSVEntry>>();
Dictionary<string, Dictionary<string, CSVEntry>> removed = new Dictionary<string, Dictionary<string, CSVEntry>>();
foreach (var f in currentValues)
{
Dictionary<string, CSVEntry> other;
inputValues.TryGetValue(f.Key, out other);
if (other == null)
{
added.Add(f.Key, f.Value);
}
else
{
Dictionary<string, CSVEntry> a = new Dictionary<string,CSVEntry>();
foreach (KeyValuePair<string, CSVEntry> s in f.Value)
{
if (other.ContainsKey(s.Key))
other.Remove(s.Key);
else
a.Add(s.Key, s.Value);
}
if (a.Count > 0)
added.Add(f.Key, a);
if (other.Count > 0)
removed.Add(f.Key, other);
}
}
Dictionary<string, Dictionary<string, string>> overrides = new Dictionary<string, Dictionary<string, string>>();
Dictionary<string, Dictionary<string, CSVEntry>> diffElems = new Dictionary<string, Dictionary<string, CSVEntry>>();
foreach (KeyValuePair<string, Dictionary<string, CSVEntry>> f in added)
foreach (KeyValuePair<string, CSVEntry> s in f.Value)
if (s.Value.Origvalue == s.Value.Value || s.Value.Value.Trim().Length == 0)
{
if (!overrides.ContainsKey(f.Key))
overrides.Add(f.Key, new Dictionary<string, string>());
if (!diffElems.ContainsKey(f.Key))
diffElems.Add(f.Key, new Dictionary<string, CSVEntry>());
overrides[f.Key].Add(s.Key, "not-updated");
diffElems[f.Key].Add(s.Key, s.Value);
}
foreach (KeyValuePair<string, Dictionary<string, CSVEntry>> f in removed)
foreach (KeyValuePair<string, CSVEntry> s in f.Value)
{
if (!overrides.ContainsKey(f.Key))
overrides.Add(f.Key, new Dictionary<string, string>());
if (!diffElems.ContainsKey(f.Key))
diffElems.Add(f.Key, new Dictionary<string, CSVEntry>());
overrides[f.Key].Add(s.Key, "unused");
diffElems[f.Key].Add(s.Key, s.Value);
}
WriteCSVFile(diffFile, culture, diffElems, overrides);
//Re-read the file
inputValues = ImportCSV(inputfile, culture, null);
//Add the new entries
foreach (KeyValuePair<string, Dictionary<string, CSVEntry>> f in added)
{
if (!inputValues.ContainsKey(f.Key))
inputValues.Add(f.Key, new Dictionary<string, CSVEntry>());
Dictionary<string, CSVEntry> o = inputValues[f.Key];
foreach (KeyValuePair<string, CSVEntry> s in f.Value)
o.Add(s.Key, s.Value);
}
foreach (KeyValuePair<string, Dictionary<string, CSVEntry>> f in currentValues)
{
Dictionary<string, CSVEntry> o = inputValues[f.Key];
foreach (KeyValuePair<string, CSVEntry> s in f.Value)
{
CSVEntry c = o[s.Key];
if (s.Value.Status == "unused")
c.Status = "unused";
else if (c.Origvalue == c.Value)
c.Status = "not-updated";
else if (string.IsNullOrEmpty(c.Value) || c.Value.Trim().Length == 0)
c.Status = "missing";
else
c.Status = "updated";
o[s.Key] = c;
}
}
//Update the removed entries
foreach (KeyValuePair<string, Dictionary<string, CSVEntry>> f in removed)
{
Dictionary<string, CSVEntry> o = inputValues[f.Key];
foreach (KeyValuePair<string, CSVEntry> s in f.Value)
{
CSVEntry c = o[s.Key];
c.Status = "unused";
o[s.Key] = c;
}
}
//Write the output file
diffFile = System.IO.Path.Combine(Application.StartupPath, "report." + culture + ".updated.csv");
WriteCSVFile(diffFile, culture, inputValues, null);
}
private static void WriteCSVFile(string filename, string culture, Dictionary<string, Dictionary<string, CSVEntry>> entries, Dictionary<string, Dictionary<string, string>> status_overrides)
{
if (status_overrides == null)
status_overrides = new Dictionary<string, Dictionary<string, string>>();
List<KeyValuePair<string, KeyValuePair<string, CSVEntry>>> sortlist = new List<KeyValuePair<string, KeyValuePair<string, CSVEntry>>>();
foreach (KeyValuePair<string, Dictionary<string, CSVEntry>> f in entries)
foreach (KeyValuePair<string, CSVEntry> s in f.Value)
sortlist.Add(new KeyValuePair<string, KeyValuePair<string, CSVEntry>>(f.Key, s));
var sortedlist = sortlist.OrderBy(x => x.Key + System.IO.Path.PathSeparator + x.Value.Key);
using (System.IO.StreamWriter sw = new System.IO.StreamWriter(filename, false, new System.Text.UTF8Encoding(false)))
{
string filenameprefix = System.IO.Path.Combine(Application.StartupPath, culture);
int pfl = filenameprefix.Length + 1;
foreach (KeyValuePair<string, KeyValuePair<string, CSVEntry>> f in sortedlist)
{
string status = f.Value.Value.Status;
if (status_overrides.ContainsKey(f.Key) && status_overrides[f.Key].ContainsKey(f.Value.Key))
status = status_overrides[f.Key][f.Value.Key];
string path = f.Key;
if (f.Key.StartsWith(filenameprefix, Duplicati.Library.Utility.Utility.ClientFilenameStringComparision))
path = f.Key.Substring(Math.Min(pfl, f.Key.Length));
if (Duplicati.Library.Utility.Utility.IsClientLinux)
path = path.Replace(System.IO.Path.DirectorySeparatorChar.ToString(), "\\");
WriteCSVLine(sw, path, f.Value.Key, status, f.Value.Value.Origvalue, f.Value.Value.Value, f.Value.Value.extraFields);
}
}
}
private static string CSV_SEPARATOR = ",";
private static void WriteCSVLine(System.IO.StreamWriter sw, string filename, string key, string status, string originalText, string translatedText, string[] extraFields)
{
sw.Write(EscapeCSVString(filename));
sw.Write(CSV_SEPARATOR);
sw.Write(EscapeCSVString(key));
sw.Write(CSV_SEPARATOR);
sw.Write(EscapeCSVString(status));
sw.Write(CSV_SEPARATOR);
sw.Write(EscapeCSVString(originalText));
sw.Write(CSV_SEPARATOR);
sw.Write(EscapeCSVString(translatedText));
if (extraFields != null && extraFields.Length > 0)
foreach (string s in extraFields)
{
sw.Write(CSV_SEPARATOR);
sw.Write(EscapeCSVString(s));
}
sw.Write("\r\n");
}
private static string EscapeCSVString(string value)
{
//.Replace("\\", "\\\\").Replace("\r\n", "\\n").Replace("\r", "\\n").Replace("\n", "\\n").Replace("\t", "\\t")
return "\"" + value.Replace("\"", "\"\"") + "\"";
}
private static void Report(string cultures)
{
foreach (string culture in GetLocaleFolders(cultures))
{
Dictionary<string, string> ignores = new Dictionary<string, string>();
string ignorefile = System.IO.Path.Combine(Application.StartupPath, "ignore." + culture + ".xml");
if (System.IO.File.Exists(ignorefile))
ignores = XDocument.Load(ignorefile).Element("root").Elements("ignore").Select(c => c.Value).ToSafeDictionary(c => c.ToLower().Trim(), ignorefile);
//Make sure we ignore empty strings
ignores[""] = "";
//Ignore common leftover values
for (int i = 1; i < 20; i++)
{
string _i = i.ToString();
ignores["textbox" + _i] = "";
ignores["combobox" + _i] = "";
ignores["checkbox" + _i] = "";
ignores["label" + _i] = "";
ignores["radiobutton" + _i] = "";
ignores["button" + _i] = "";
ignores["toolstrip" + _i] = "";
ignores["toolstripbutton" + _i] = "";
ignores["statusstrip" + _i] = "";
}
XDocument report = new XDocument(
new XElement("root")
);
XElement reportRoot = report.Element("root");
IEnumerable<ResXFileInfo> files = GetResXList(culture);
var missingFiles = files.Where(c => !System.IO.File.Exists(c.TargetFile));
var existingFiles = files.Where(c => System.IO.File.Exists(c.TargetFile));
var extraFiles = from x in Duplicati.Library.Utility.Utility.EnumerateFiles(System.IO.Path.Combine(Application.StartupPath, culture))
where
x.EndsWith("." + culture + ".resx")
&&
!files.Select(c => c.TargetFile).Contains(x)
select x;
reportRoot.Add(
new XElement("files",
new XElement("missing",
from x in missingFiles
select new XElement("file", x.TargetFile)
),
new XElement("unused",
from x in extraFiles
select new XElement("file", x)
)
)
);
foreach(ResXFileInfo inf in existingFiles)
{
IEnumerable<XElement> sourceElements = XDocument.Load(inf.SourceFile).Element("root").Elements("data");
IEnumerable<XElement> targetElements = XDocument.Load(inf.TargetFile).Element("root").Elements("data");
if (inf.IsForm)
{
//Look only for strings
Func<XElement, bool> filter =
c =>
!c.Attribute("name").Value.StartsWith(">>")
&&
c.Attribute("mimetype") == null
&&
(
c.Attribute("type") == null
||
c.Attribute("type").Value == "System.String, mscorlib"
||
c.Attribute("type").Value == "System.String"
);
sourceElements = sourceElements.Where(filter);
targetElements = targetElements.Where(filter);
}
//Filter the source and target before proceeding
sourceElements = sourceElements.Where(c => !ignores.ContainsKey(c.Element("value").Value.Trim().ToLower()) && !c.Element("value").Value.Trim().ToLower().StartsWith("..\\resources\\") && !c.Element("value").Value.Trim().ToLower().StartsWith("../resources/"));
targetElements = targetElements.Where(c => !ignores.ContainsKey(c.Element("value").Value.Trim().ToLower()) && !c.Element("value").Value.Trim().ToLower().StartsWith("..\\resources\\") && !c.Element("value").Value.Trim().ToLower().StartsWith("../resources/"));
var sourceVals = sourceElements.ToSafeDictionary(c => c.Attribute("name").Value, inf.SourceFile);
var targetVals = targetElements.ToSafeDictionary(c => c.Attribute("name").Value, inf.TargetFile);
var missing = sourceVals.Where(c => !targetVals.ContainsKey(c.Key)).ToList();
var unused = targetVals.Where(c => !sourceVals.ContainsKey(c.Key)).ToList();
var notUpdated = sourceVals.Where(c =>
targetVals.ContainsKey(c.Key)
&&
targetVals[c.Key].Element("value").Value == c.Value.Element("value").Value).ToList();
var updated = sourceVals.Where(c =>
!missing.Contains(c)
&&
!unused.Contains(c)
&&
!notUpdated.Contains(c)
&&
targetVals.ContainsKey(c.Key)
);
var yy = sourceVals.Values.Where(x => x.Value.Contains("System.Draw")).Concat(targetVals.Values.Where(x => x.Value.Contains("System.Draw")));
if (yy.Count() != 0)
{
}
reportRoot.Add(
new XElement("file",
new XAttribute("filename", inf.TargetFile),
new XElement("missing",
from x in missing
select new XElement("item",
new XAttribute("name", x.Key),
x.Value.Element("value").Value
)
),
new XElement("unused",
from x in unused
select new XElement("item",
new XAttribute("name", x.Key),
x.Value.Element("value").Value
)
),
new XElement("not-updated",
from x in notUpdated
select new XElement("item",
new XAttribute("name", x.Key),
x.Value.Element("value").Value
)
),
new XElement("updated",
from x in updated
select new XElement("item",
new XAttribute("name", x.Key),
new XElement("original",
x.Value.Element("value").Value
),
new XElement("translated",
targetVals[x.Key].Element("value").Value
)
)
)
)
);
}
report.Save(System.IO.Path.Combine(Application.StartupPath, "report." + culture + ".xml"));
}
}
private static void Create(string culture)
{
System.Globalization.CultureInfo ci = System.Globalization.CultureInfo.GetCultureInfo(culture); //Validate
string folder = System.IO.Path.Combine(Application.StartupPath, culture);
if (!System.IO.Directory.Exists(folder))
System.IO.Directory.CreateDirectory(folder);
Update(culture);
}
public class ResXFileInfo
{
public string SourceFile { get; set; }
public string TargetFile { get; set; }
public string NetrualTargetFile { get; set; }
public bool IsForm { get; set; }
}
public static IEnumerable<ResXFileInfo> GetResXList(string culture)
{
List<ResXFileInfo> res = new List<ResXFileInfo>();
foreach (XElement conf in XDocument.Load(System.IO.Path.Combine(Application.StartupPath, "configuration.xml")).Element("root").Elements("configuration"))
{
string sourcefolder = conf.Element("sourcefolder").Value;
if (Duplicati.Library.Utility.Utility.IsClientLinux)
sourcefolder = sourcefolder.Replace("\\", System.IO.Path.DirectorySeparatorChar.ToString());
string outputfolder = System.IO.Path.GetFullPath(Duplicati.Library.Utility.Utility.AppendDirSeparator(System.IO.Path.Combine(Application.StartupPath, culture)));
sourcefolder = Duplicati.Library.Utility.Utility.AppendDirSeparator(System.IO.Path.GetFullPath(sourcefolder));
foreach (XElement fn in conf.Elements("assembly"))
{
string subfolder = fn.Attribute("folder").Value;
if (Duplicati.Library.Utility.Utility.IsClientLinux)
subfolder = subfolder.Replace("\\", System.IO.Path.DirectorySeparatorChar.ToString());
foreach (string s in Duplicati.Library.Utility.Utility.EnumerateFiles(System.IO.Path.Combine(sourcefolder, subfolder)))
{
if (s.ToLower().StartsWith(Application.StartupPath.ToLower()))
continue;
if (s.EndsWith(".resx"))
{
string targetNameNeutral = System.IO.Path.Combine(outputfolder, s.Substring(sourcefolder.Length));
if (!System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(targetNameNeutral)))
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(targetNameNeutral));
string targetFilename = targetNameNeutral.Substring(0, targetNameNeutral.Length - "resx".Length) + culture + ".resx";
if (res.Where(x => x.TargetFile == targetFilename).FirstOrDefault() == null)
res.Add(new ResXFileInfo()
{
SourceFile = s,
IsForm = System.IO.File.Exists(System.IO.Path.ChangeExtension(s, ".cs")),
NetrualTargetFile = targetNameNeutral,
TargetFile = targetFilename
});
}
}
}
}
return res;
}
private static void Update(string cultures)
{
foreach (string culture in GetLocaleFolders(cultures))
{
foreach (ResXFileInfo inf in GetResXList(culture))
{
if (inf.IsForm)
System.IO.File.Copy(inf.SourceFile, inf.NetrualTargetFile, true); //Copy the updated resx
if (System.IO.File.Exists(inf.TargetFile))
{
//Merge, forms are auto-merged, in that they depend on the neutral .resx file
if (!inf.IsForm)
{
XDocument targetDoc = XDocument.Load(inf.TargetFile);
XNode insertTarget = targetDoc.Element("root").LastNode;
var sourceVals = XDocument.Load(inf.SourceFile).Element("root").Elements("data").ToSafeDictionary(c => c.Attribute("name").Value, inf.SourceFile);
var targetVals = targetDoc.Element("root").Elements("data").ToSafeDictionary(c => c.Attribute("name").Value, inf.TargetFile);
bool updated = false;
foreach (var item in sourceVals)
if (!targetVals.ContainsKey(item.Key))
{
updated = true;
insertTarget.AddAfterSelf(new XElement(item.Value));
}
if (updated)
targetDoc.Save(inf.TargetFile);
}
}
else
{
if (inf.IsForm)
using (System.IO.StreamWriter sw = new System.IO.StreamWriter(inf.TargetFile))
sw.Write(Properties.Resources.Empty_resx);
else
{
if (Duplicati.Library.Utility.Utility.IsClientLinux)
{
var doc = XDocument.Load(inf.SourceFile);
var entries = from x in doc.Element("root").Elements("data")
where
x.Attribute("type") != null
&&
x.Attribute("type").Value == "System.Resources.ResXFileRef, System.Windows.Forms"
select x;
foreach(var e in entries)
{
string name = e.Element("value").Value;
int ixpos = name.IndexOf(";");
string path = name.Substring(0, ixpos);
path = path.Replace ("\\", System.IO.Path.DirectorySeparatorChar.ToString());
e.Element("value").Value = path + name.Substring(ixpos);
}
doc.Save(inf.TargetFile);
} else {
System.IO.File.Copy(inf.SourceFile, inf.TargetFile);
}
}
}
foreach (var item in from y in
(from x in XDocument.Load(inf.SourceFile).Element("root").Elements("data")
where
x.Attribute("type") != null
&&
x.Attribute("type").Value == "System.Resources.ResXFileRef, System.Windows.Forms"
select x)
let relname = y.Element("value").Value.Substring(0, y.Element("value").Value.IndexOf(";"))
let sourceRes = System.IO.Path.GetFullPath(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(inf.SourceFile), relname)).Replace("\\", System.IO.Path.DirectorySeparatorChar.ToString())
let targetRes = System.IO.Path.GetFullPath(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(inf.TargetFile), relname)).Replace("\\", System.IO.Path.DirectorySeparatorChar.ToString())
select new { sourceRes, targetRes }
)
{
if (!System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(item.targetRes)))
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(item.targetRes));
if (!System.IO.File.Exists(item.targetRes))
{
if (Duplicati.Library.Utility.Utility.IsClientLinux && !System.IO.File.Exists(item.sourceRes))
{
string basefolder = System.IO.Path.GetDirectoryName(item.sourceRes);
string sourcefile = System.IO.Directory.GetFiles (basefolder).FirstOrDefault (x => item.sourceRes.Equals(x, StringComparison.InvariantCultureIgnoreCase));
if (sourcefile == null)
Console.WriteLine ("Source file not found: " + item.sourceRes);
else
System.IO.File.Copy (sourcefile, item.targetRes);
} else {
System.IO.File.Copy(item.sourceRes, item.targetRes);
}
}
}
}
string batchfilename = System.IO.Path.Combine(System.IO.Path.Combine(Application.StartupPath, culture), "gui update.bat");
System.IO.File.WriteAllText(batchfilename, Properties.Resources.Batchjob_bat, System.Text.Encoding.Default);
}
}
private static void Clean(string cultureReq)
{
string root = Duplicati.Library.Utility.Utility.AppendDirSeparator(Application.StartupPath);
if (!string.IsNullOrEmpty(cultureReq))
root = System.IO.Path.Combine(root, cultureReq);
foreach (string s in Duplicati.Library.Utility.Utility.EnumerateFiles(Application.StartupPath))
if (System.IO.Path.GetDirectoryName(s) != Application.StartupPath && (System.IO.Path.GetExtension(s) == ".resources" || System.IO.Path.GetExtension(s) == ".dll"))
System.IO.File.Delete(s);
else if (System.IO.Path.GetExtension(s) == ".resx")
{
//Remove the extra files required for resgen.exe
string culture = s.Substring(root.Length);
culture = culture.Substring(0, culture.IndexOf(System.IO.Path.DirectorySeparatorChar));
if (!s.ToLower().EndsWith("." + culture.ToLower() + ".resx") && System.IO.File.Exists(s.Substring(0, s.Length - "resx".Length) + culture + ".resx"))
System.IO.File.Delete(s);
}
}
private static void Compile(string cultureReq)
{
XDocument doc = XDocument.Load(System.IO.Path.Combine(Application.StartupPath, "configuration.xml"));
foreach (XElement conf in doc.Element("root").Elements("configuration"))
{
string keyfile = conf.Element("keyfile") == null ? null : conf.Element("keyfile").Value;
string versionassembly = conf.Element("versionassembly").Value;
string outputfolder = conf.Element("outputfolder").Value;
string productname = conf.Element("productname").Value;
string fxVer = conf.Element("frameworkVersion") == null ? "4.0" : conf.Element("frameworkVersion").Value;
Console.WriteLine("Compiling for framework: " + fxVer);
foreach (XElement n in conf.Elements("assembly"))
{
string assemblyName = n.Attribute("name").Value;
string folder = n.Attribute("folder").Value;
string @namespace = n.Attribute("namespace") == null ? assemblyName : n.Attribute("namespace").Value;
foreach (string culture in GetLocaleFolders(cultureReq))
{
List<string> excludes = (from x in n.Elements("exclude")
let fullpath = System.IO.Path.Combine(System.IO.Path.Combine(Application.StartupPath, culture), x.Value)
select fullpath).ToList();
string outfolder = System.IO.Path.GetFullPath(System.IO.Path.Combine(outputfolder, culture));
if (!System.IO.Directory.Exists(outfolder))
System.IO.Directory.CreateDirectory(outfolder);
ResXCompiler.CompileResxFiles(System.IO.Path.Combine(System.IO.Path.Combine(Application.StartupPath, culture), folder), excludes, @namespace, System.IO.Path.Combine(outfolder, assemblyName + ".resources.dll"), System.IO.Path.GetFullPath(versionassembly), keyfile, culture, productname, fxVer);
}
}
}
}
}
}
@@ -1,52 +0,0 @@
#region Disclaimer / License
// Copyright (C) 2015, The Duplicati Team
// http://www.duplicati.com, info@duplicati.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("LocalizationTool")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Duplicati Team")]
[assembly: AssemblyProduct("LocalizationTool")]
[assembly: AssemblyCopyright("Copyright © Duplicati Team 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("6f59216b-26e5-48cc-907b-16a97c7625df")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
[assembly: AssemblyVersion("2.0.0.7")]
[assembly: AssemblyFileVersion("2.0.0.7")]
@@ -1,105 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.544
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace LocalizationTool.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LocalizationTool.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to @echo off
///
///echo Processing updates, please wait ...
///
///set FOLDERNAME=
///for /f &quot;tokens=*&quot; %%a in (&quot;%CD%&quot;) do set FOLDERNAME=%%~na
///
///..\LocalizationTool.exe update %FOLDERNAME%
///..\LocalizationTool.exe guiupdate %FOLDERNAME%.
/// </summary>
internal static string Batchjob_bat {
get {
return ResourceManager.GetString("Batchjob.bat", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
///&lt;root&gt;
/// &lt;!--
/// Microsoft ResX Schema
///
/// Version 2.0
///
/// The primary goals of this format is to allow a simple XML format
/// that is mostly human readable. The generation and parsing of the
/// various data types are done through the TypeConverter classes
/// associated with the data types.
///
/// Example:
///
/// ... ado.net/XML headers &amp; schema ...
/// &lt;resheader name=&quot;resmimetype&quot;&gt;text/microsoft-resx&lt;/resheader&gt;
/// &lt;resheader n [rest of string was truncated]&quot;;.
/// </summary>
internal static string Empty_resx {
get {
return ResourceManager.GetString("Empty_resx", resourceCulture);
}
}
}
}
@@ -1,127 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="Batchjob.bat" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Batchjob.bat.txt;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
</data>
<data name="Empty_resx" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Empty_resx.txt;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
</data>
</root>
@@ -1,26 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.544
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace LocalizationTool.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}
@@ -1,5 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles />
<Settings />
</SettingsFile>
@@ -1,230 +0,0 @@
#region Disclaimer / License
// Copyright (C) 2015, The Duplicati Team
// http://www.duplicati.com, info@duplicati.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Configuration;
namespace LocalizationTool
{
public static class ResXCompiler
{
private static string ExecuteAndRead(string command, string arguments)
{
var psi = new System.Diagnostics.ProcessStartInfo(command, arguments);
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
var pi = System.Diagnostics.Process.Start(psi);
pi.WaitForExit(5000);
if (pi.HasExited)
return pi.StandardOutput.ReadToEnd().Trim();
return null;
}
public static void CompileResxFiles(string folder, List<string> excludeFolders, string @namespace, string assemblyname, string versionAssembly, string keyfile, string culture, string productname, string fxVer)
{
string resgenexe;
string alexe;
if (Duplicati.Library.Utility.Utility.IsClientLinux)
{
resgenexe = ExecuteAndRead("which", "resgen");
alexe = ExecuteAndRead("which", "al");
}
else //Windows
{
//Order of these paths is also the search order
string[] known_sdk_paths = null;
if (fxVer == "2.0")
{
known_sdk_paths = new string[]
{
Environment.ExpandEnvironmentVariables("%WINDIR%\\Microsoft.Net\\Framework\\v2.0.50727\\"),
Environment.ExpandEnvironmentVariables("%PROGRAMFILES%\\Microsoft.NET\\SDK\\v2.0\\bin\\"),
Environment.ExpandEnvironmentVariables("%PROGRAMFILES(X86)%\\Microsoft.NET\\SDK\\v2.0\\bin\\"),
};
}
else if (fxVer == "3.0")
{
known_sdk_paths = new string[]
{
Environment.ExpandEnvironmentVariables("%WINDIR%\\Microsoft.Net\\Framework\\v3.0\\"),
Environment.ExpandEnvironmentVariables("%PROGRAMFILES%\\Microsoft SDKs\\Windows\\v6.0A\\bin\\"),
Environment.ExpandEnvironmentVariables("%PROGRAMFILES(X86)%\\Microsoft SDKs\\Windows\\v6.0A\\bin\\"),
};
}
else if (fxVer == "3.5")
{
known_sdk_paths = new string[]
{
Environment.ExpandEnvironmentVariables("%WINDIR%\\Microsoft.Net\\Framework\\v3.5\\"),
Environment.ExpandEnvironmentVariables("%PROGRAMFILES%\\Microsoft SDKs\\Windows\\v7.0A\\bin\\"),
Environment.ExpandEnvironmentVariables("%PROGRAMFILES(X86)%\\Microsoft SDKs\\Windows\\v7.0A\\bin\\"),
};
}
else
{
known_sdk_paths = new string[]
{
Environment.ExpandEnvironmentVariables("%WINDIR%\\Microsoft.Net\\Framework\\v4.0\\"),
Environment.ExpandEnvironmentVariables("%WINDIR%\\Microsoft.Net\\Framework\\v4.0.30319\\"),
Environment.ExpandEnvironmentVariables("%PROGRAMFILES%\\Microsoft SDKs\\Windows\\v8.0A\\bin\\"),
Environment.ExpandEnvironmentVariables("%PROGRAMFILES%\\Microsoft SDKs\\Windows\\v8.0A\\bin\\NETFX 4.0 Tools\\"),
Environment.ExpandEnvironmentVariables("%PROGRAMFILES%\\Microsoft SDKs\\Windows\\v7.1\\Bin\\NETFX 4.0 Tools\\"),
Environment.ExpandEnvironmentVariables("%PROGRAMFILES(X86)%\\Microsoft SDKs\\Windows\\v8.0A\\bin\\"),
Environment.ExpandEnvironmentVariables("%PROGRAMFILES(X86)%\\Microsoft SDKs\\Windows\\v8.0A\\bin\\NETFX 4.0 Tools\\"),
Environment.ExpandEnvironmentVariables("%PROGRAMFILES(X86)%\\Microsoft SDKs\\Windows\\v7.1\\Bin\\NETFX 4.0 Tools\\"),
};
}
resgenexe = "resgen.exe";
alexe = "al.exe";
foreach(var p in known_sdk_paths)
if (System.IO.File.Exists(System.IO.Path.Combine(p, resgenexe)))
{
resgenexe = System.IO.Path.Combine(p, resgenexe);
break;
}
foreach (var p in known_sdk_paths)
if (System.IO.File.Exists(System.IO.Path.Combine(p, alexe)))
{
alexe = System.IO.Path.Combine(p, alexe);
break;
}
}
if (!System.IO.File.Exists(resgenexe))
{
Console.WriteLine("Unable to locate file: {0}", resgenexe);
Console.WriteLine("This can be fixed by installing a microsoft platform SDK, or visual studio (express is fine)");
return;
}
if (!System.IO.File.Exists(alexe))
{
Console.WriteLine("Unable to locate file: {0}", alexe);
Console.WriteLine("This can be fixed by installing the .Net framework version 2.0");
return;
}
if (!string.IsNullOrEmpty(keyfile) && Duplicati.Library.Utility.Utility.IsClientLinux)
keyfile = keyfile.Replace("\\", System.IO.Path.DirectorySeparatorChar.ToString());
List<string> resources = new List<string>();
folder = Duplicati.Library.Utility.Utility.AppendDirSeparator(folder);
foreach (string s in Duplicati.Library.Utility.Utility.EnumerateFiles(folder))
{
if (s.ToLower().EndsWith("." + culture.ToLower() + ".resx"))
{
if (excludeFolders.Any(xf => s.ToLower().StartsWith(Duplicati.Library.Utility.Utility.AppendDirSeparator(xf).ToLower())))
continue;
string resname = System.IO.Path.ChangeExtension(s, ".resources");
if (!System.IO.File.Exists(resname) || System.IO.File.GetLastWriteTime(resname) < System.IO.File.GetLastWriteTime(s))
{
Console.WriteLine("Compiling: " + s);
System.Diagnostics.ProcessStartInfo pi = new System.Diagnostics.ProcessStartInfo(resgenexe, "\"" + s + "\"");
pi.CreateNoWindow = true;
pi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
pi.RedirectStandardOutput = true;
pi.RedirectStandardError = true;
pi.UseShellExecute = false;
pi.WorkingDirectory = System.IO.Path.GetDirectoryName(s);
System.Diagnostics.Process pr = System.Diagnostics.Process.Start(pi);
pr.WaitForExit();
if (pr.ExitCode != 0)
{
Console.WriteLine("Error");
Console.WriteLine(pr.StandardOutput.ReadToEnd());
Console.WriteLine(pr.StandardError.ReadToEnd());
throw new Exception("Resgen failure: " + s);
}
}
else
Console.WriteLine("Not modified: " + s);
resources.Add(resname);
}
}
if (resources.Count == 0)
return;
if (!System.IO.File.Exists(versionAssembly) && Duplicati.Library.Utility.Utility.IsClientLinux)
versionAssembly = versionAssembly.Replace("\\", System.IO.Path.DirectorySeparatorChar.ToString());
if (!System.IO.File.Exists(versionAssembly))
{
Console.WriteLine("Unable to locate file: {0}", versionAssembly);
Console.WriteLine("This can be fixed by compiling the application or modifying the file configuration.xml");
return;
}
using (Duplicati.Library.Utility.TempFile tf = new Duplicati.Library.Utility.TempFile())
{
using (System.IO.StreamWriter sw = new System.IO.StreamWriter(tf))
{
System.Reflection.Assembly asm = System.Reflection.Assembly.ReflectionOnlyLoadFrom(versionAssembly);
sw.WriteLine("/t:lib");
sw.WriteLine("/out:\"" + assemblyname + "\"");
sw.WriteLine("/product:\"" + productname + "\"");
sw.WriteLine("/title:\"" + productname + "\"");
sw.WriteLine("/version:" + asm.GetName().Version.ToString());
if (!string.IsNullOrEmpty(keyfile))
sw.WriteLine("/keyfile:\"" + keyfile + "\"");
sw.WriteLine("/culture:" + culture);
foreach (string s in resources)
{
string resname = s.Substring(folder.Length);
resname = resname.Replace("\\", ".");
resname = resname.Replace(" ", "_");
resname = @namespace + "." + resname;
sw.WriteLine("/embed:\"" + s + "\"," + resname);
}
}
Console.WriteLine("Linking ...");
System.Diagnostics.ProcessStartInfo pi = new System.Diagnostics.ProcessStartInfo(alexe, "@\"" + tf + "\"");
pi.CreateNoWindow = true;
pi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
System.Diagnostics.Process pr = System.Diagnostics.Process.Start(pi);
pr.WaitForExit();
if (pr.ExitCode != 0)
throw new Exception("Linker failure");
}
}
}
}
@@ -1,9 +0,0 @@
@echo off
echo Processing updates, please wait ...
set FOLDERNAME=
for /f "tokens=*" %%a in ("%CD%") do set FOLDERNAME=%%~na
..\LocalizationTool.exe update %FOLDERNAME%
..\LocalizationTool.exe guiupdate %FOLDERNAME%
@@ -1,120 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
@@ -1,311 +0,0 @@
namespace LocalizationTool
{
partial class UpdateGUI
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.panel1 = new System.Windows.Forms.Panel();
this.panel2 = new System.Windows.Forms.Panel();
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
this.treeView1 = new System.Windows.Forms.TreeView();
this.label1 = new System.Windows.Forms.Label();
this.textBox1 = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
this.textBox2 = new System.Windows.Forms.TextBox();
this.label3 = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.label5 = new System.Windows.Forms.Label();
this.label6 = new System.Windows.Forms.Label();
this.panel3 = new System.Windows.Forms.Panel();
this.label7 = new System.Windows.Forms.Label();
this.panel4 = new System.Windows.Forms.Panel();
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.button3 = new System.Windows.Forms.Button();
this.label8 = new System.Windows.Forms.Label();
this.panel1.SuspendLayout();
this.panel2.SuspendLayout();
this.splitContainer1.Panel1.SuspendLayout();
this.splitContainer1.Panel2.SuspendLayout();
this.splitContainer1.SuspendLayout();
this.panel3.SuspendLayout();
this.panel4.SuspendLayout();
this.SuspendLayout();
//
// panel1
//
this.panel1.Controls.Add(this.button3);
this.panel1.Controls.Add(this.button2);
this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.panel1.Location = new System.Drawing.Point(0, 264);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(459, 45);
this.panel1.TabIndex = 0;
//
// panel2
//
this.panel2.Controls.Add(this.label6);
this.panel2.Controls.Add(this.label5);
this.panel2.Controls.Add(this.label4);
this.panel2.Controls.Add(this.label3);
this.panel2.Dock = System.Windows.Forms.DockStyle.Top;
this.panel2.Location = new System.Drawing.Point(0, 0);
this.panel2.Name = "panel2";
this.panel2.Size = new System.Drawing.Size(459, 32);
this.panel2.TabIndex = 1;
//
// splitContainer1
//
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainer1.Location = new System.Drawing.Point(0, 32);
this.splitContainer1.Name = "splitContainer1";
//
// splitContainer1.Panel1
//
this.splitContainer1.Panel1.Controls.Add(this.treeView1);
this.splitContainer1.Panel1.Controls.Add(this.panel4);
//
// splitContainer1.Panel2
//
this.splitContainer1.Panel2.Controls.Add(this.panel3);
this.splitContainer1.Panel2.Controls.Add(this.label7);
this.splitContainer1.Size = new System.Drawing.Size(459, 232);
this.splitContainer1.SplitterDistance = 227;
this.splitContainer1.TabIndex = 2;
//
// treeView1
//
this.treeView1.Dock = System.Windows.Forms.DockStyle.Fill;
this.treeView1.HideSelection = false;
this.treeView1.Location = new System.Drawing.Point(0, 0);
this.treeView1.Name = "treeView1";
this.treeView1.Size = new System.Drawing.Size(227, 200);
this.treeView1.TabIndex = 0;
this.treeView1.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeView1_AfterSelect);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(16, 8);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(25, 13);
this.label1.TabIndex = 0;
this.label1.Text = "Key";
//
// textBox1
//
this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBox1.Location = new System.Drawing.Point(56, 8);
this.textBox1.Name = "textBox1";
this.textBox1.ReadOnly = true;
this.textBox1.Size = new System.Drawing.Size(42, 20);
this.textBox1.TabIndex = 1;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(16, 40);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(34, 13);
this.label2.TabIndex = 2;
this.label2.Text = "Value";
//
// textBox2
//
this.textBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBox2.Location = new System.Drawing.Point(16, 56);
this.textBox2.Multiline = true;
this.textBox2.Name = "textBox2";
this.textBox2.Size = new System.Drawing.Size(82, 136);
this.textBox2.TabIndex = 3;
this.textBox2.TextChanged += new System.EventHandler(this.textBox2_TextChanged);
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(8, 8);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(83, 13);
this.label3.TabIndex = 0;
this.label3.Text = "Items remaining:";
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(96, 8);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(37, 13);
this.label4.TabIndex = 1;
this.label4.Text = "12345";
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(152, 8);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(36, 13);
this.label5.TabIndex = 2;
this.label5.Text = "Folder";
//
// label6
//
this.label6.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.label6.AutoEllipsis = true;
this.label6.Location = new System.Drawing.Point(200, 8);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(256, 16);
this.label6.TabIndex = 3;
this.label6.Text = "label6";
//
// panel3
//
this.panel3.Controls.Add(this.textBox2);
this.panel3.Controls.Add(this.label2);
this.panel3.Controls.Add(this.label1);
this.panel3.Controls.Add(this.textBox1);
this.panel3.Location = new System.Drawing.Point(8, 0);
this.panel3.Name = "panel3";
this.panel3.Size = new System.Drawing.Size(104, 208);
this.panel3.TabIndex = 4;
this.panel3.Visible = false;
//
// label7
//
this.label7.Anchor = System.Windows.Forms.AnchorStyles.None;
this.label7.Location = new System.Drawing.Point(67, 92);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(100, 16);
this.label7.TabIndex = 5;
this.label7.Text = "Click on an entry";
this.label7.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// panel4
//
this.panel4.Controls.Add(this.label8);
this.panel4.Controls.Add(this.button1);
this.panel4.Dock = System.Windows.Forms.DockStyle.Bottom;
this.panel4.Location = new System.Drawing.Point(0, 200);
this.panel4.Name = "panel4";
this.panel4.Size = new System.Drawing.Size(227, 32);
this.panel4.TabIndex = 6;
//
// button1
//
this.button1.Location = new System.Drawing.Point(5, 8);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 6;
this.button1.Text = "&Next";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button2
//
this.button2.Anchor = System.Windows.Forms.AnchorStyles.Top;
this.button2.Location = new System.Drawing.Point(144, 8);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(75, 23);
this.button2.TabIndex = 0;
this.button2.Text = "&OK";
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// button3
//
this.button3.Anchor = System.Windows.Forms.AnchorStyles.Top;
this.button3.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.button3.Location = new System.Drawing.Point(240, 8);
this.button3.Name = "button3";
this.button3.Size = new System.Drawing.Size(75, 23);
this.button3.TabIndex = 1;
this.button3.Text = "&Cancel";
this.button3.UseVisualStyleBackColor = true;
this.button3.Click += new System.EventHandler(this.button3_Click);
//
// label8
//
this.label8.AutoSize = true;
this.label8.Location = new System.Drawing.Point(96, 16);
this.label8.Name = "label8";
this.label8.Size = new System.Drawing.Size(134, 13);
this.label8.TabIndex = 2;
this.label8.Text = "Press ALT+N for next entry";
//
// UpdateGUI
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.button3;
this.ClientSize = new System.Drawing.Size(459, 309);
this.Controls.Add(this.splitContainer1);
this.Controls.Add(this.panel2);
this.Controls.Add(this.panel1);
this.Name = "UpdateGUI";
this.Text = "Localization updater";
this.Load += new System.EventHandler(this.UpdateGUI_Load);
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.UpdateGUI_FormClosing);
this.panel1.ResumeLayout(false);
this.panel2.ResumeLayout(false);
this.panel2.PerformLayout();
this.splitContainer1.Panel1.ResumeLayout(false);
this.splitContainer1.Panel2.ResumeLayout(false);
this.splitContainer1.ResumeLayout(false);
this.panel3.ResumeLayout(false);
this.panel3.PerformLayout();
this.panel4.ResumeLayout(false);
this.panel4.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Panel panel2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.SplitContainer splitContainer1;
private System.Windows.Forms.TreeView treeView1;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label6;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.Panel panel3;
private System.Windows.Forms.Button button3;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.Panel panel4;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Label label8;
}
}
@@ -1,233 +0,0 @@
#region Disclaimer / License
// Copyright (C) 2015, The Duplicati Team
// http://www.duplicati.com, info@duplicati.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml.Linq;
namespace LocalizationTool
{
public partial class UpdateGUI : Form
{
private string m_reportfile;
private int m_totalcount;
private Dictionary<string, XElement> m_updates = new Dictionary<string, XElement>();
public UpdateGUI(string reportfile)
{
InitializeComponent();
m_reportfile = reportfile;
}
private void UpdateGUI_Load(object sender, EventArgs e)
{
panel3.Dock = DockStyle.Fill;
string culturename = System.IO.Path.GetExtension(System.IO.Path.GetFileNameWithoutExtension(m_reportfile)).Substring(1);
this.Text = "Localizing culture " + culturename;
var files = XDocument.Load(m_reportfile).Element("root").Elements("file").Where(n =>
(n.Element("missing") != null && n.Element("missing").Elements().Count() > 0)
||
(n.Element("not-updated") != null && n.Element("not-updated").Elements().Count() > 0)
);
string rootfolder = Duplicati.Library.Utility.Utility.AppendDirSeparator(System.IO.Path.Combine(Application.StartupPath, culturename));
treeView1.BeginUpdate();
treeView1.Nodes.Clear();
foreach (XElement x in files)
{
TreeNodeCollection cl = treeView1.Nodes;
string relpath = x.Attribute("filename").Value.Substring(rootfolder.Length);
foreach (string s in relpath.Split(System.IO.Path.DirectorySeparatorChar))
{
bool found = false;
foreach (TreeNode n in cl)
if (n.Text == s)
{
cl = n.Nodes;
found = true;
break;
}
if (!found)
{
TreeNode nn = new TreeNode(s);
nn.ForeColor = Color.DarkBlue;
cl.Add(nn);
cl = nn.Nodes;
}
}
foreach (XElement el in x.Element("missing").Elements("item").Union(x.Element("not-updated").Elements("item")))
{
//These show up occasionally, but mut not be translated
if (
el.Attribute("name").Value.EndsWith(".Name")
||
el.Attribute("name").Value.EndsWith(".MappingName")
)
continue;
string val = el.Value;
if (val.Length > 100)
val = val.Substring(0, 97) + "...";
TreeNode tn = new TreeNode(el.Attribute("name").Value + ": " + val);
tn.ForeColor = Color.DarkRed;
tn.Tag = el;
cl.Add(tn);
m_totalcount++;
}
}
treeView1.EndUpdate();
treeView1.ExpandAll();
if (treeView1.Nodes.Count > 0)
{
TreeNode top = treeView1.Nodes[0];
while (top.Nodes.Count > 0)
top = top.Nodes[0];
treeView1.SelectedNode = top;
}
label4.Text = m_totalcount.ToString();
label6.Text = System.IO.Path.Combine(Application.StartupPath, culturename);
this.WindowState = FormWindowState.Maximized;
}
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
if (treeView1.SelectedNode == null || treeView1.SelectedNode.Tag as XElement == null)
panel3.Visible = false;
else
{
panel3.Visible = true;
XElement el = treeView1.SelectedNode.Tag as XElement;
panel3.Tag = null;
textBox1.Text = el.Attribute("name").Value;
textBox2.Text = el.Value.Replace(Environment.NewLine, "\n").Replace("\n", Environment.NewLine);
try
{
textBox2.Focus();
textBox2.SelectAll();
}
catch { }
panel3.Tag = el;
}
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
if (panel3.Tag as XElement == null)
return;
XElement el = panel3.Tag as XElement;
el.Value = textBox2.Text;
string dictkey = el.Parent.Parent.Attribute("filename").Value + "@" + el.Attribute("name").Value;
m_updates[dictkey] = el;
label4.Text = (m_totalcount - m_updates.Count).ToString();
if (treeView1.SelectedNode != null && treeView1.SelectedNode.Tag == el)
treeView1.SelectedNode.ForeColor = Color.DarkGreen;
}
private void button3_Click(object sender, EventArgs e)
{
if (m_updates.Count > 0)
if (MessageBox.Show(this, "Close and loose all changes?", Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button3) != DialogResult.Yes)
return;
this.DialogResult = DialogResult.Cancel;
this.Close();
}
private void button1_Click(object sender, EventArgs e)
{
TreeNode t = treeView1.SelectedNode;
if (t == null)
return;
List<TreeNode> visited = new List<TreeNode>();
while (t != null)
{
if (t.Nodes.Count > 0 && !visited.Contains(t))
t = t.Nodes[0];
else if (t.NextNode != null)
t = t.NextNode;
else
{
if (t.Parent != null && !visited.Contains(t.Parent))
visited.Add(t.Parent);
t = t.Parent;
}
if (t != null && t.ForeColor == Color.DarkRed)
{
treeView1.SelectedNode = t;
return;
}
}
MessageBox.Show(this, "No more items", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void button2_Click(object sender, EventArgs e)
{
foreach (XElement el in m_updates.Values)
{
XDocument file = XDocument.Load(el.Parent.Parent.Attribute("filename").Value);
var insertEl = file.Element("root").Elements("data").Where(c => c.Attribute("name").Value == el.Attribute("name").Value).FirstOrDefault();
if (insertEl != null)
insertEl.Element("value").Value = el.Value;
else
file.Element("root").Elements().Last().AddAfterSelf(
new XElement("data",
new XAttribute("name", el.Attribute("name").Value),
new XAttribute(XNamespace.Xml + "space", "preserve"),
new XElement("value", el.Value)
)
);
file.Save(el.Parent.Parent.Attribute("filename").Value);
}
m_updates.Clear();
this.DialogResult = DialogResult.OK;
this.Close();
}
private void UpdateGUI_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing || e.CloseReason == CloseReason.FormOwnerClosing)
if (m_updates.Count > 0)
if (MessageBox.Show(this, "Close and loose all changes?", Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button3) != DialogResult.Yes)
{
e.Cancel = true;
return;
}
}
}
}
@@ -1,120 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
@@ -1,5 +0,0 @@
<?xml version="1.0"?>
<configuration>
<configSections>
</configSections>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup></configuration>
@@ -1,29 +0,0 @@
using System;
using System.Collections.Generic;
namespace LocalizationTool2
{
public class LocalizationEntry
{
public LocalizationEntry()
{
SourceLocations = new List<string>();
}
public LocalizationEntry(string text, string file, int position)
: this()
{
SourceString = text;
AddSource(file, position);
}
internal void AddSource(string file, int linepos)
{
SourceLocations.Add(string.Format("{0}:{1}", file, linepos));
}
public string SourceString { get; set; }
public List<string> SourceLocations { get; set; }
}
}
@@ -1,46 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>{6795DFBA-B494-4D6F-A79C-9118EF1CC109}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>LocalizationTool2</RootNamespace>
<AssemblyName>LocalizationTool2</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ExternalConsole>true</ExternalConsole>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ExternalConsole>true</ExternalConsole>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="Newtonsoft.Json">
<HintPath>packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="LocalizationEntry.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
@@ -1,46 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LocalizationTool2", "LocalizationTool2.csproj", "{6795DFBA-B494-4D6F-A79C-9118EF1CC109}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6795DFBA-B494-4D6F-A79C-9118EF1CC109}.Debug|x86.ActiveCfg = Debug|x86
{6795DFBA-B494-4D6F-A79C-9118EF1CC109}.Debug|x86.Build.0 = Debug|x86
{6795DFBA-B494-4D6F-A79C-9118EF1CC109}.Release|x86.ActiveCfg = Release|x86
{6795DFBA-B494-4D6F-A79C-9118EF1CC109}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
Policies = $0
$0.TextStylePolicy = $1
$1.inheritsSet = VisualStudio
$1.scope = text/plain
$1.FileWidth = 120
$1.EolMarker = Windows
$1.inheritsScope = text/plain
$0.CSharpFormattingPolicy = $2
$2.NewLinesForBracesInProperties = True
$2.NewLinesForBracesInAccessors = True
$2.NewLinesForBracesInAnonymousMethods = True
$2.NewLinesForBracesInControlBlocks = True
$2.NewLinesForBracesInAnonymousTypes = True
$2.NewLinesForBracesInObjectCollectionArrayInitializers = True
$2.NewLinesForBracesInLambdaExpressionBody = True
$2.NewLineForElse = True
$2.NewLineForCatch = True
$2.NewLineForFinally = True
$2.NewLineForMembersInObjectInit = True
$2.NewLineForMembersInAnonymousTypes = True
$2.NewLineForClausesInQuery = True
$2.SpacingAfterMethodDeclarationName = False
$2.SpaceAfterMethodCallName = False
$2.SpaceBeforeOpenSquareBracket = False
$2.inheritsSet = Mono
$2.inheritsScope = text/x-csharp
$2.scope = text/x-csharp
EndGlobalSection
EndGlobal
-94
View File
@@ -1,94 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace LocalizationTool2
{
class MainClass
{
public static int Main(string[] args)
{
var sourcefolder = Environment.CurrentDirectory;
if (args == null || args.Length != 0)
sourcefolder = args[0];
sourcefolder = Path.GetFullPath(sourcefolder.Replace("~", Environment.GetEnvironmentVariable("HOME")));
var targetfolder = sourcefolder;
if (args == null || args.Length > 1)
targetfolder = Path.GetFullPath(args[1].Replace("~", Environment.GetEnvironmentVariable("HOME")));
if (!Directory.Exists(sourcefolder))
{
Console.WriteLine("No such directory: {0}", sourcefolder);
return 1;
}
sourcefolder = Path.GetFullPath(sourcefolder);
Console.WriteLine("Using directory {0}, scanning ....", sourcefolder);
var searchlist = new Dictionary<string, Regex>();
searchlist.Add("html", new Regex("((\\{\\{)|(ng-bind-html\\s*=\\s*\"))\\s*\\'(?<sourcestring>(\\\\\\'|[^\\'])+)\\'\\s*\\|\\s*localize(\\s|\\:|\\})", RegexOptions.Multiline | RegexOptions.IgnoreCase));
searchlist.Add("js", new Regex("Localization\\.localize\\(\\s*((\\'(?<sourcestring>(\\\\\\'|[^\\'])+)\\')|(\\\"(?<sourcestring>(\\\\\\\"|[^\\\"])+)\\\"))", RegexOptions.Multiline | RegexOptions.IgnoreCase));
searchlist.Add("cs", new Regex("LC.L\\s*\\(((@\\s*\"(?<sourcestring>(\"\"|[^\"])+))|(\"(?<sourcestring>(\\\\\"|[^\"])+)))\"\\s*(\\)|,)", RegexOptions.Multiline | RegexOptions.IgnoreCase));
var map = new Dictionary<string, LocalizationEntry>();
if (!sourcefolder.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
sourcefolder += Path.DirectorySeparatorChar;
foreach (var ext in searchlist.Keys)
{
var re = searchlist[ext];
foreach (var f in Directory.GetFiles(sourcefolder, "*." + ext, SearchOption.AllDirectories))
{
var txt = File.ReadAllText(f);
foreach (Match match in re.Matches(txt))
{
var linepos = txt.Substring(match.Index).Count(x => x == '\n');
var str = match.Groups["sourcestring"].Value.Replace("\n", "\\n").Replace("\r", "\\r");
LocalizationEntry le;
if (!map.TryGetValue(str, out le))
map[str] = new LocalizationEntry(str, f.Substring(sourcefolder.Length), linepos);
else
le.AddSource(Path.GetFileName(f), linepos);
}
}
}
//File.WriteAllText(Path.Combine(sourcefolder, "translations.json"), Newtonsoft.Json.JsonConvert.SerializeObject(map.Values.OrderBy(x => x.SourceLocations.FirstOrDefault()).ToArray(), Newtonsoft.Json.Formatting.Indented));
//File.WriteAllText(Path.Combine(sourcefolder, "translations-list.json"), Newtonsoft.Json.JsonConvert.SerializeObject(map.Select(x => x.Key).OrderBy(x => x).ToArray(), Newtonsoft.Json.Formatting.Indented));
File.WriteAllLines(
Path.Combine(targetfolder, "localization.po"),
map.OrderBy(x => x.Key).Select(x => x.Value).SelectMany(x => new string[] {
"#: " + x.SourceLocations.FirstOrDefault(),
string.Format("msgid \"{0}\"", (x.SourceString ?? "")),
string.Format("msgstr \"{0}\"", (x.SourceString ?? "")),
""
}),
System.Text.Encoding.UTF8
);
Console.WriteLine("Wrote {0} strings to {1}", map.Count, Path.Combine(targetfolder, "localization.po"));
/*File.WriteAllLines(
Path.Combine(sourcefolder, "localization-by-file.po"),
map.OrderBy(x => x.Value.SourceLocations.FirstOrDefault()).Select(x => x.Value).SelectMany(x => new string[] {
"#: " + x.SourceLocations.FirstOrDefault(),
string.Format("msgid \"{0}\"", (x.SourceString ?? "")),
string.Format("msgstr \"{0}\"", (x.SourceString ?? "")),
""
}),
System.Text.Encoding.UTF8
);*/
return 0;
}
}
}
@@ -1,26 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle("LocalizationTool2")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("kenneth")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion("2.0.0.7")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("")]
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
</packages>
@@ -135,12 +135,12 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
<Compile Include="WinFormsRunner.cs" />
<EmbeddedResource Include="Resources\CloseMenuIcon.png" />
<EmbeddedResource Include="Resources\Pause.png" />
<EmbeddedResource Include="Resources\Play.png" />
<EmbeddedResource Include="Resources\StatusMenuIcon.png" />
<Compile Include="RumpsRunner.cs" />
<None Include="packages.config" />
<EmbeddedResource Include="Resources\context_menu_open.ico" />
<EmbeddedResource Include="Resources\context_menu_pause.ico" />
<EmbeddedResource Include="Resources\context_menu_play.ico" />
<EmbeddedResource Include="Resources\context_menu_quit.ico" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Library\Backend\CloudFiles\Duplicati.Library.Backend.CloudFiles.csproj">
@@ -81,10 +81,10 @@ namespace Duplicati.GUI.TrayIcon
public const string PauseIcon = "Resources.TrayNormalPause.ico";
public const string WorkingIcon = "Resources.TrayWorking.ico";
public static Bitmap Pause { get { return LoadImage("Resources.Pause.png"); } }
public static Bitmap Play { get { return LoadImage("Resources.Play.png"); } }
public static Bitmap CloseMenuIcon { get { return LoadImage("Resources.CloseMenuIcon.png"); } }
public static Bitmap StatusMenuIcon { get { return LoadImage("Resources.StatusMenuIcon.png"); } }
public static Bitmap Pause { get { return LoadImage("Resources.context_menu_pause.ico"); } }
public static Bitmap Play { get { return LoadImage("Resources.context_menu_play.ico"); } }
public static Bitmap CloseMenuIcon { get { return LoadImage("Resources.context_menu_quit.ico"); } }
public static Bitmap StatusMenuIcon { get { return LoadImage("Resources.context_menu_open.ico"); } }
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 661 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 515 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 667 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

@@ -145,6 +145,15 @@ namespace Duplicati.GUI.TrayIcon.Windows
{
if (m_onSingleClick != null)
m_onSingleClick();
else if (m_trayIcon != null && m_trayIcon.ContextMenuStrip != null)
{
// Show context menu on left-click as we have nothing happening otherwise
try
{
typeof(NotifyIcon).GetMethod("ShowContextMenu", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).Invoke(m_trayIcon, null);
}
catch { }
}
}
private void m_trayIcon_DoubleClick(object sender, EventArgs e)
@@ -33,6 +33,10 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="NGettext, Version=0.6.1.0, Culture=neutral, PublicKeyToken=08d3d1c89dfd2985, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\NGettext.0.6.2\lib\net45\NGettext.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
@@ -41,28 +45,15 @@
<Compile Include="ILocalizationService.cs" />
<Compile Include="MockLocalizationService.cs" />
<Compile Include="Short.cs" />
<Compile Include="PoLocalizationService.cs" />
<Compile Include="MoLocalizationService.cs" />
<Compile Include="LocalizationContext.cs" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Folder Include="Localizations\" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="..\..\..\Localizations\localization-da.po">
<Link>Localizations\localization-da.po</Link>
</EmbeddedResource>
<EmbeddedResource Include="..\..\..\Localizations\localization-de.po">
<Link>Localizations\localization-de.po</Link>
</EmbeddedResource>
<EmbeddedResource Include="..\..\..\Localizations\localization-en.po">
<Link>Localizations\localization-en.po</Link>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Content Include="..\..\..\Localizations\localization.po">
<Link>localization.po</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<ItemGroup>
<EmbeddedResource Include="..\..\..\Localizations\duplicati\*.mo" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
@@ -62,11 +62,6 @@ namespace Duplicati.Library.Localization
/// <param name="message">The string to localize</param>
/// <param name="args">The arguments</param>
string Localize(string message, params object[] args);
/// <summary>
/// Gets a lookup table with all strings
/// </summary>
IDictionary<string, string> AllStrings { get; }
}
}
@@ -20,6 +20,7 @@ using System.Linq;
using System.Globalization;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Duplicati.Library.Localization.Short;
namespace Duplicati.Library.Localization
{
@@ -52,7 +53,8 @@ namespace Duplicati.Library.Localization
{
if (ci == null)
return null;
LC.setCulture(ci);
return new LocalizationContext(ci);
}
@@ -112,7 +114,7 @@ namespace Duplicati.Library.Localization
ILocalizationService service;
if (!Services.TryGetValue(ci, out service))
service = Services[ci] = new PoLocalizationService(ci);
service = Services[ci] = new MoLocalizationService(ci);
return service;
}
@@ -134,7 +136,15 @@ namespace Duplicati.Library.Localization
/// </summary>
public static IEnumerable<string> SupportedCultures
{
get { return PoLocalizationService.SupportedCultures; }
get { return MoLocalizationService.SupportedCultures; }
}
/// <summary>
/// Returns true if the culture has localization support
/// </summary>
public static Boolean isCultureSupported(CultureInfo culture)
{
return MoLocalizationService.isCultureSupported(culture);
}
}
}
@@ -1,254 +1,226 @@
// Copyright (C) 2016, The Duplicati Team
// http://www.duplicati.com, info@duplicati.com
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Text.RegularExpressions;
namespace Duplicati.Library.Localization
{
/// <summary>
/// Class for reading embedded PO files
/// </summary>
public class PoLocalizationService : ILocalizationService
{
/// <summary>
/// The environment variable used to locate PO files
/// </summary>
public const string LOCALIZATIONDIR_ENVNAME = "LOCALIZATION_FOLDER";
/// <summary>
/// A cached copy of all strings
/// </summary>
private Dictionary<string, string> m_messages = new Dictionary<string, string>();
/// <summary>
/// Path to search for extra .po files in
/// </summary>
public static string[] SearchPaths =
string.IsNullOrWhiteSpace(AppDomain.CurrentDomain.GetData(LOCALIZATIONDIR_ENVNAME) as string)
? new string[] { Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) }
: (AppDomain.CurrentDomain.GetData(LOCALIZATIONDIR_ENVNAME) as string).Split(new char[] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries);
/// <summary>
/// Assembly to look for .po files in
/// </summary>
public static Assembly SearchAssembly = Assembly.GetExecutingAssembly();
/// <summary>
/// Regular expression to match a PO line entry
/// </summary>
private static readonly Regex PO_MATCHER = new Regex(@"(?<key>\w+(\[(?<index>[0-9]+)\])?)\s+""(?<value>(?:\\.|""""|[^""\\])*)""");
/// <summary>
/// Regular expression to match a locale from a filename
/// </summary>
private static readonly Regex CI_MATCHER = new Regex(@"localization-(?<culture>" + LocalizationService.CI_MATCHER + @")\.po");
/// <summary>
/// Initializes a new instance of the <see cref="T:Duplicati.Library.Localization.PoLocalizationService"/> class.
/// </summary>
/// <param name="ci">The culture to find.</param>
public PoLocalizationService(CultureInfo ci)
{
var filenames = new string[] {
// Load the generic country version first
string.Format("localization-{0}.po", ci.Name),
// Then the specialized version with overrides
string.Format("localization-{0}.po", ci.TwoLetterISOLanguageName)
};
foreach(var fn in filenames)
{
// Use embedded version first
if (SearchAssembly != null)
{
// Find the localization streams inside the assembly
var names =
from name in SearchAssembly.GetManifestResourceNames()
let m = CI_MATCHER.Match(name)
let c = m.Success && string.Equals(m.Value, fn, StringComparison.InvariantCultureIgnoreCase) ? LocalizationService.ParseCulture(m.Groups["culture"].Value) : null
where c != null
select name;
foreach (var sn in names)
using (var s = SearchAssembly.GetManifestResourceStream(sn))
if (s != null)
using (var sr = new StreamReader(s, System.Text.Encoding.UTF8, true))
ParseStream(sr);
}
// Then override with external
foreach(var sp in SearchPaths)
if (!string.IsNullOrWhiteSpace(sp) && File.Exists(Path.Combine(sp, fn)))
using (var fs = new StreamReader(Path.Combine(sp, fn), System.Text.Encoding.UTF8, true))
ParseStream(fs);
}
}
/// <summary>
/// Parses a PO file by reading each line
/// </summary>
/// <param name="rd">The reader to extract data from.</param>
private void ParseStream(TextReader rd)
{
string ln;
string msgid = null;
while ((ln = rd.ReadLine()) != null)
{
var m = PO_MATCHER.Match(ln);
if (m.Success)
{
var key = m.Groups["key"].Value;
var value = m.Groups["value"].Value;
if (!string.IsNullOrEmpty(value))
value = value.Replace("\"\"", "\"").Replace("\\\"", "\"").Replace("\\t", "\t").Replace("\\r", "\r").Replace("\\n", "\n");
if (string.Equals(key, "msgid", StringComparison.OrdinalIgnoreCase))
{
msgid = value;
}
else if (string.Equals(key, "msgstr", StringComparison.OrdinalIgnoreCase))
{
m_messages[msgid] = value;
}
}
}
}
/// <summary>
/// Performs the actual translation
/// </summary>
/// <param name="msg">The message to translate.</param>
private string Transform(string msg)
{
string res;
if (string.IsNullOrWhiteSpace(msg) || !m_messages.TryGetValue(msg, out res))
return msg;
return res;
}
/// <summary>
/// Localizes the string similar to how string.Format works
/// </summary>
/// <param name="message">The string to localize</param>
/// <returns>The localized string</returns>
public string Localize(string message)
{
return string.Format(Transform(message));
}
/// <summary>
/// Localizes the string similar to how string.Format works
/// </summary>
/// <param name="message">The string to localize</param>
/// <param name="arg0">The first argument</param>
/// <returns>The localized string</returns>
public string Localize(string message, object arg0)
{
return string.Format(Transform(message), arg0);
}
/// <summary>
/// Localizes the string similar to how string.Format works
/// </summary>
/// <param name="message">The string to localize</param>
/// <param name="arg0">The first argument</param>
/// <param name="arg1">The second argument</param>
/// <returns>The localized string</returns>
public string Localize(string message, object arg0, object arg1)
{
return string.Format(Transform(message), arg0, arg1);
}
/// <summary>
/// Localizes the string similar to how string.Format works
/// </summary>
/// <param name="message">The string to localize</param>
/// <param name="arg0">The first argument</param>
/// <param name="arg1">The second argument</param>
/// <param name="arg2">The third argument</param>
/// <returns>The localized string</returns>
public string Localize(string message, object arg0, object arg1, object arg2)
{
return string.Format(Transform(message), arg0, arg1, arg2);
}
/// <summary>
/// Localizes the string similar to how string.Format works
/// </summary>
/// <param name="message">The string to localize</param>
/// <param name="args">The arguments</param>
public string Localize(string message, params object[] args)
{
return string.Format(Transform(message), args);
}
/// <summary>
/// Gets all strings.
/// </summary>
public IDictionary<string, string> AllStrings { get { return m_messages; } }
private static string[] m_supportedcultures = null;
/// <summary>
/// Gets a list of all the supported cultures
/// </summary>
public static IEnumerable<string> SupportedCultures
{
get
{
if (m_supportedcultures == null)
{
var lst = new string[0].AsEnumerable();
if (SearchAssembly != null)
lst = lst.Union(SearchAssembly.GetManifestResourceNames());
foreach (var sp in SearchPaths)
if (Directory.Exists(sp))
lst = lst.Union(Directory.GetFiles(sp, "localization-*.po"));
var allcultures =
from name in lst
let m = CI_MATCHER.Match(name)
let ci = m.Success ? LocalizationService.ParseCulture(m.Groups["culture"].Value) : null
where ci != null
select ci;
m_supportedcultures =
allcultures
.Select(x => x.TwoLetterISOLanguageName).Distinct().OrderBy(x => x)
.Union(
allcultures.Select(x => x.Name).Distinct().OrderBy(x => x)
).ToArray();
}
return m_supportedcultures;
}
}
}
}
// Copyright (C) 2016, The Duplicati Team
// http://www.duplicati.com, info@duplicati.com
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Text.RegularExpressions;
using NGettext;
namespace Duplicati.Library.Localization
{
/// <summary>
/// Class for reading embedded MO files
/// </summary>
public class MoLocalizationService : ILocalizationService
{
/// <summary>
/// The catalog containing the translations
/// </summary>
private ICatalog catalog = new Catalog();
/// <summary>
/// The environment variable used to locate MO files
/// </summary>
public const string LOCALIZATIONDIR_ENVNAME = "LOCALIZATION_FOLDER";
/// <summary>
/// Path to search for extra .mo files in
/// </summary>
public static string[] SearchPaths =
string.IsNullOrWhiteSpace(AppDomain.CurrentDomain.GetData(LOCALIZATIONDIR_ENVNAME) as string)
? new string[] { Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) }
: (AppDomain.CurrentDomain.GetData(LOCALIZATIONDIR_ENVNAME) as string).Split(new char[] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries);
/// <summary>
/// Assembly to look for .mo files in
/// </summary>
public static Assembly SearchAssembly = Assembly.GetExecutingAssembly();
/// <summary>
/// Regular expression to match a locale from a filename
/// </summary>
private static readonly Regex CI_MATCHER = new Regex(@"localization-(?<culture>" + LocalizationService.CI_MATCHER + @")\.mo");
/// <summary>
/// Initializes a new instance of the <see cref="T:Duplicati.Library.Localization.PoLocalizationService"/> class.
/// </summary>
/// <param name="ci">The culture to find.</param>
public MoLocalizationService(CultureInfo ci)
{
var filenames = new string[] {
// Load the specialized version first
string.Format("localization-{0}.mo", ci.Name),
// Then try the generic language version
string.Format("localization-{0}.mo", ci.TwoLetterISOLanguageName)
};
foreach(var fn in filenames)
{
// search first in external files
foreach (var sp in SearchPaths)
{
if (!string.IsNullOrWhiteSpace(sp) && File.Exists(Path.Combine(sp, fn)))
{
using (var moFileStream = File.OpenRead(Path.Combine(sp, fn)))
catalog = new Catalog(moFileStream, ci);
return;
}
}
// search in embedded files
if (SearchAssembly != null)
{
// Find the localization streams inside the assembly
var names =
from name in SearchAssembly.GetManifestResourceNames()
let m = CI_MATCHER.Match(name)
let c = m.Success && string.Equals(m.Value, fn, StringComparison.InvariantCultureIgnoreCase) ? LocalizationService.ParseCulture(m.Groups["culture"].Value) : null
where c != null
select name;
foreach (var sn in names)
using (var s = SearchAssembly.GetManifestResourceStream(sn))
if (s != null)
{
catalog = new Catalog(s, ci);
return;
}
}
}
}
/// <summary>
/// Localizes the string similar to how string.Format works
/// </summary>
/// <param name="message">The string to localize</param>
/// <returns>The localized string</returns>
public string Localize(string message)
{
return catalog.GetString(message);
}
/// <summary>
/// Localizes the string similar to how string.Format works
/// </summary>
/// <param name="message">The string to localize</param>
/// <param name="arg0">The first argument</param>
/// <returns>The localized string</returns>
public string Localize(string message, object arg0)
{
return catalog.GetString(message, arg0);
}
/// <summary>
/// Localizes the string similar to how string.Format works
/// </summary>
/// <param name="message">The string to localize</param>
/// <param name="arg0">The first argument</param>
/// <param name="arg1">The second argument</param>
/// <returns>The localized string</returns>
public string Localize(string message, object arg0, object arg1)
{
return catalog.GetString(message, arg0, arg1);
}
/// <summary>
/// Localizes the string similar to how string.Format works
/// </summary>
/// <param name="message">The string to localize</param>
/// <param name="arg0">The first argument</param>
/// <param name="arg1">The second argument</param>
/// <param name="arg2">The third argument</param>
/// <returns>The localized string</returns>
public string Localize(string message, object arg0, object arg1, object arg2)
{
return catalog.GetString(message, arg0, arg1, arg2);
}
/// <summary>
/// Localizes the string similar to how string.Format works
/// </summary>
/// <param name="message">The string to localize</param>
/// <param name="args">The arguments</param>
public string Localize(string message, params object[] args)
{
return catalog.GetString(message, args);
}
private static IEnumerable<CultureInfo> m_supportedcultures = null;
/// <summary>
/// Gets a list of all the supported cultures
/// </summary>
private static IEnumerable<CultureInfo> SupportedCultureInfos
{
get
{
if (m_supportedcultures == null)
{
var lst = new string[0].AsEnumerable();
if (SearchAssembly != null)
lst = lst.Union(SearchAssembly.GetManifestResourceNames());
foreach (var sp in SearchPaths)
if (Directory.Exists(sp))
lst = lst.Union(Directory.GetFiles(sp, "localization-*.mo"));
var allcultures =
from name in lst
let m = CI_MATCHER.Match(name)
let ci = m.Success ? LocalizationService.ParseCulture(m.Groups["culture"].Value) : null
where ci != null
select ci;
m_supportedcultures = allcultures.Concat(new[] { new CultureInfo("en") })
.Distinct();
}
return m_supportedcultures;
}
}
/// <summary>
/// Gets a list of all the supported cultures
/// </summary>
public static IEnumerable<string> SupportedCultures
{
get
{
return SupportedCultureInfos
.Select(x => x.Name).Distinct()
.OrderBy(x => x)
.ToList();
}
}
/// <summary>
/// Returns true if the culture has localization support
/// </summary>
public static Boolean isCultureSupported(CultureInfo culture)
{
foreach (var supportedCulture in SupportedCultureInfos)
{
if (supportedCulture.TwoLetterISOLanguageName == culture.TwoLetterISOLanguageName)
return true;
}
return false;
}
}
}
@@ -77,11 +77,6 @@ namespace Duplicati.Library.Localization
{
return string.Format(message, args);
}
/// <summary>
/// Gets all strings.
/// </summary>
public IDictionary<string, string> AllStrings { get { return new Dictionary<string, string>(); } }
}
}
+9
View File
@@ -97,6 +97,15 @@ namespace Duplicati.Library.Localization.Short
/// </summary>
private static ILocalizationService LS = LocalizationService.Current;
/// <summary>
/// Sets the culture
/// </summary>
/// <param name="ci">CultureInfo</param>
public static void setCulture(System.Globalization.CultureInfo ci)
{
LS = LocalizationService.Get(ci);
}
/// <summary>
/// Localizes the string similar to how string.Format works
/// </summary>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NGettext" version="0.6.2" targetFramework="net45" />
</packages>
+62 -7
View File
@@ -82,21 +82,76 @@ namespace Duplicati.Library.Main
/// </summary>
private bool m_doResetLocale;
/// <summary>
/// The caffeinate process runner
/// </summary>
private System.Diagnostics.Process m_caffeinate;
/// <summary>
/// This gets called whenever execution of an operation is started or stopped; it currently handles the AllowSleep option
/// </summary>
/// <param name="isRunning">Flag indicating execution state</param>
private void OperationRunning(bool isRunning)
{
if (m_options != null && !m_options.AllowSleep && !Duplicati.Library.Utility.Utility.IsClientLinux)
if (m_options != null && !m_options.AllowSleep)
{
try
if (Duplicati.Library.Utility.Utility.IsClientWindows)
{
Win32.SetThreadExecutionState(Win32.EXECUTION_STATE.ES_CONTINUOUS | (isRunning ? Win32.EXECUTION_STATE.ES_SYSTEM_REQUIRED : 0));
try
{
Win32.SetThreadExecutionState(Win32.EXECUTION_STATE.ES_CONTINUOUS | (isRunning ? Win32.EXECUTION_STATE.ES_SYSTEM_REQUIRED : 0));
}
catch (Exception ex)
{
Logging.Log.WriteMessage("Failed to set sleep prevention", Logging.LogMessageType.Warning, ex);
}
}
catch (Exception ex)
else if (Duplicati.Library.Utility.Utility.IsClientOSX)
{
Logging.Log.WriteMessage("Failed to set sleep prevention", Logging.LogMessageType.Warning, ex);
if (isRunning)
{
try
{
if (m_caffeinate == null)
{
// -s prevents sleep on AC, -i prevents sleep generally
var psi = new System.Diagnostics.ProcessStartInfo("caffeinate", "-s");
psi.RedirectStandardInput = true;
psi.UseShellExecute = false;
m_caffeinate = System.Diagnostics.Process.Start(psi);
}
}
catch (Exception ex)
{
Logging.Log.WriteMessage("Failed to set sleep prevention", Logging.LogMessageType.Warning, ex);
}
}
else
{
try
{
if (m_caffeinate != null && !m_caffeinate.HasExited)
{
// Send CTRL+C
m_caffeinate.StandardInput.Write("\x3");
m_caffeinate.StandardInput.Flush();
m_caffeinate.WaitForExit(500);
if (!m_caffeinate.HasExited)
{
m_caffeinate.Kill();
m_caffeinate.WaitForExit(500);
if (!m_caffeinate.HasExited)
throw new Exception("Failed to kill the caffeinate process");
}
}
}
catch (Exception ex)
{
Logging.Log.WriteMessage("Failed to unset sleep prevention", Logging.LogMessageType.Warning, ex);
}
}
}
}
}
@@ -151,13 +206,13 @@ namespace Duplicati.Library.Main
for(int i = 0; i < sources.Count - 1; i++)
{
for(int j = i + 1; j < sources.Count; j++)
if (sources[i].Equals(sources[j], Library.Utility.Utility.IsFSCaseSensitive ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase))
if (sources[i].Equals(sources[j], Library.Utility.Utility.ClientFilenameStringComparision))
{
result.AddVerboseMessage("Removing duplicate source: {0}", sources[j]);
sources.RemoveAt(j);
j--;
}
else if (sources[i].StartsWith(sources[j], Library.Utility.Utility.IsFSCaseSensitive ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase))
else if (sources[i].StartsWith(sources[j], Library.Utility.Utility.ClientFilenameStringComparision))
{
bool includes;
bool excludes;
@@ -43,6 +43,12 @@ namespace Duplicati.Library.Main.Operation
public static Snapshots.ISnapshotService GetSnapshot(string[] sources, Options options, ILogWriter log)
{
if (!Library.Utility.Utility.IsClientWindows && options.RawOptions.ContainsKey("hyperv-backup-vm"))
log.AddWarning("hyperv-backup-vm is efective only on Windows OS.", null);
if (Library.Utility.Utility.IsClientWindows && options.RawOptions.ContainsKey("hyperv-backup-vm") && options.SnapShotStrategy == Options.OptimizationStrategy.Off)
throw new Exception("Snapshot strategy cannot be Off when backuping Hyper-V using hyperv-backup-vm"); //VSS is required for Hyper-V backups
try
{
if (options.SnapShotStrategy != Options.OptimizationStrategy.Off)
@@ -52,6 +58,8 @@ namespace Duplicati.Library.Main.Operation
{
if (options.SnapShotStrategy == Options.OptimizationStrategy.Required)
throw;
else if (options.SnapShotStrategy == Options.OptimizationStrategy.On && options.RawOptions.ContainsKey("hyperv-backup-vm"))
throw; //VSS is required for Hyper-V backups
else if (options.SnapShotStrategy == Options.OptimizationStrategy.On)
log.AddWarning(Strings.Common.SnapshotFailedError(ex.ToString()), ex);
}
+1 -3
View File
@@ -254,7 +254,6 @@ namespace Duplicati.Library.Main
"vss-use-mapping",
"usn-policy",
"hyperv-backup-vm",
"hyperv-backup-no-merge",
"symlink-policy",
"hardlink-policy",
"exclude-files-attributes",
@@ -450,7 +449,6 @@ namespace Duplicati.Library.Main
new CommandLineArgument("vss-use-mapping", CommandLineArgument.ArgumentType.Boolean, Strings.Options.VssusemappingShort, Strings.Options.VssusemappingLong, "false"),
new CommandLineArgument("usn-policy", CommandLineArgument.ArgumentType.Enumeration, Strings.Options.UsnpolicyShort, Strings.Options.UsnpolicyLong, "off", null, Enum.GetNames(typeof(OptimizationStrategy))),
new CommandLineArgument("hyperv-backup-vm", CommandLineArgument.ArgumentType.String, Strings.Options.HypervbackupvmShort, Strings.Options.HypervbackupvmLong),
new CommandLineArgument("hyperv-backup-no-merge", CommandLineArgument.ArgumentType.Boolean, Strings.Options.HypervbackupnomergeShort, Strings.Options.HypervbackupnomergeLong),
new CommandLineArgument("encryption-module", CommandLineArgument.ArgumentType.String, Strings.Options.EncryptionmoduleShort, Strings.Options.EncryptionmoduleLong, "aes"),
new CommandLineArgument("compression-module", CommandLineArgument.ArgumentType.String, Strings.Options.CompressionmoduleShort, Strings.Options.CompressionmoduleLong, "zip"),
@@ -465,7 +463,7 @@ namespace Duplicati.Library.Main
new CommandLineArgument("log-level", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Enumeration, Strings.Options.LoglevelShort, Strings.Options.LoglevelLong, "Warning", null, Enum.GetNames(typeof(Duplicati.Library.Logging.LogMessageType))),
new CommandLineArgument("list-verify-uploads", CommandLineArgument.ArgumentType.Boolean, Strings.Options.ListverifyuploadsShort, Strings.Options.ListverifyuploadsShort, "false"),
new CommandLineArgument("allow-sleep", CommandLineArgument.ArgumentType.Boolean, Strings.Options.AllowsleepShort, Strings.Options.AllowsleepShort, "false"),
new CommandLineArgument("allow-sleep", CommandLineArgument.ArgumentType.Boolean, Strings.Options.AllowsleepShort, Strings.Options.AllowsleepLong, "false"),
new CommandLineArgument("no-connection-reuse", CommandLineArgument.ArgumentType.Boolean, Strings.Options.NoconnectionreuseShort, Strings.Options.NoconnectionreuseLong, "false"),
new CommandLineArgument("quota-size", CommandLineArgument.ArgumentType.Size, Strings.Options.QuotasizeShort, Strings.Options.QuotasizeLong),
+4 -5
View File
@@ -36,7 +36,8 @@ namespace Duplicati.Library.Main.Strings
public static string DisablefiletimecheckShort { get { return LC.L(@"Disable checks based on file time"); } }
public static string RestorepathLong { get { return LC.L(@"By default, files will be restored in the source folders, use this option to restore to another folder"); } }
public static string RestorepathShort { get { return LC.L(@"Restore to another folder"); } }
public static string AllowsleepShort { get { return LC.L(@"Allow system to enter sleep power modes for inactivity during backup/restore operations (Windows only)"); } }
public static string AllowsleepShort { get { return LC.L(@"Toggles system sleep mode"); } }
public static string AllowsleepLong { get { return LC.L(@"Allow system to enter sleep power modes for inactivity during backup/restore operations (Windows/OSX only)"); } }
public static string ThrottledownloadLong { get { return LC.L(@"By setting this value you can limit how much bandwidth Duplicati consumes for downloads. Setting this limit can make the backups take longer, but will make Duplicati less intrusive."); } }
public static string ThrottledownloadShort { get { return LC.L(@"Max number of kilobytes to download pr. second"); } }
public static string ThrottleuploadLong { get { return LC.L(@"By setting this value you can limit how much bandwidth Duplicati consumes for uploads. Setting this limit can make the backups take longer, but will make Duplicati less intrusive."); } }
@@ -217,10 +218,8 @@ namespace Duplicati.Library.Main.Strings
public static string ConcurrencyblockhashersLong { get { return LC.L(@"Use this option to set the number of processes that perform hashing of data."); } }
public static string ConcurrencycompressorsShort{ get { return LC.L(@"Specify the number of concurrent compression processes"); } }
public static string ConcurrencycompressorsLong { get { return LC.L(@"Use this option to set the number of processes that perform compression of output data."); } }
public static string HypervbackupvmShort { get { return LC.L(@"Perform backup of HyperV machines (Windows only)"); } }
public static string HypervbackupvmLong { get { return LC.L(@"Use this option to specify the names of machines to include in the backup. Specify multiple machine names with a semicolon separator."); } }
public static string HypervbackupnomergeShort { get { return LC.L(@"Do not merge the HyperV VHD (Windows only)"); } }
public static string HypervbackupnomergeLong { get { return LC.L(@"Use this option to disable the merge of HyperV VHD's prior to performing the backup."); } }
public static string HypervbackupvmShort { get { return LC.L(@"Perform backup of Hyper-V machines (Windows only)"); } }
public static string HypervbackupvmLong { get { return LC.L(@"Use this option to specify the IDs of machines to include in the backup. Specify multiple machine IDs with a semicolon separator. (You can use this Powershell command to get ID 'Get-VM | ft VMName, ID')"); } }
}
internal static class Common
+140 -269
View File
@@ -3,317 +3,188 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Management;
using System.Text;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
namespace Duplicati.Library.Snapshots
{
public class HyperVGuest
{
public string Name { get; }
public string ID { get; }
public List<string> DataPaths { get; }
public HyperVGuest(string Name, string ID, List<string> DataPaths)
{
this.Name = Name;
this.ID = ID;
this.DataPaths = DataPaths;
}
}
//Original code: HVBackup Project
public class HyperVUtility
{
private static class WmiClasses
{
internal const string MsVM_VSMS = "MsVM_VirtualSystemManagementService";
internal const string MsVM_VSSD = "MsVM_VirtualSystemSettingData";
internal const string MsVM_IMS = "MsVM_ImageManagementService";
internal const string MsVM_RASD = "MsVM_ResourceAllocationSettingData";
}
private static class Methods
{
internal const string DefineVirtualSystem = "DefineVirtualSystem";
internal const string ModifyVirtualSystem = "ModifyVirtualSystem";
internal const string GetVirtualHardDiskInfo = "GetVirtualHardDiskInfo";
internal const string MergeVirtualHardDisk = "MergeVirtualHardDisk";
}
private static class ReturnCode
{
internal const UInt16 ERROR_SUCCESS = 0;
internal const UInt16 ERROR_JOBSTARTED = 4096;
}
private static class JobState
{
internal const UInt16 Starting = 3;
internal const UInt16 Running = 4;
internal const UInt16 Completed = 7;
}
private readonly ManagementScope _wmiScope;
private readonly string _vmIdField;
private readonly string _wmiHost = "localhost"; //We could backup remote Hyper-V Machines in the future
private readonly string _wmiHost = "localhost";
private readonly bool _wmiv2Namespace;
public List<string> RequestedVmNames = new List<string>();
readonly IDictionary<string, string> _hyperVMachines = new Dictionary<string, string>();
public HyperVUtility()
{
//Set the namespace depending off host OS
_wmiv2Namespace = Environment.OSVersion.Version.Major >= 6 && Environment.OSVersion.Version.Minor >= 2;
//Set the scope to use in MWI. V2 for Server 2012 or newer.
//Set the scope to use in WMI. V2 for Server 2012 or newer.
_wmiScope = _wmiv2Namespace
? new ManagementScope(string.Format("\\\\{0}\\root\\virtualization\\v2", _wmiHost))
: new ManagementScope(string.Format("\\\\{0}\\root\\virtualization", _wmiHost));
//Set the VM ID Selector Field for the WMI Query
_vmIdField = _wmiv2Namespace ? "VirtualSystemIdentifier" : "SystemName";
}
public HyperVUtility(List<string> reqVmNames)
{
_wmiv2Namespace = Environment.OSVersion.Version.Major >= 6 && Environment.OSVersion.Version.Minor >= 2;
_wmiScope = _wmiv2Namespace
? new ManagementScope(string.Format("\\\\{0}\\root\\virtualization\\v2", _wmiHost))
: new ManagementScope(string.Format("\\\\{0}\\root\\virtualization", _wmiHost));
_vmIdField = _wmiv2Namespace ? "VirtualSystemIdentifier" : "SystemName";
//Store requested VM's
RequestedVmNames = reqVmNames;
Logging.Log.WriteMessage(string.Format("Using WMI provider {0}", _wmiScope.Path), Logging.LogMessageType.Profiling);
}
/// <summary>
/// We query the Hyper-V for all requested Virtual Machines
/// </summary>
/// <returns>List of Hyper-V Machines</returns>
public IDictionary<string, string> GetHyperVMachines()
{
var moCollection = GetWmiObjects(WmiQueryHyperVMachines());
foreach (var mObject in moCollection)
using (mObject)
_hyperVMachines.Add((string) mObject[_vmIdField], (string) mObject["ElementName"]);
return _hyperVMachines;
}
/// <summary>
/// We build up the wmi-query to retrieve all Virtual Machines
/// </summary>
/// <returns>WMI query</returns>
private string WmiQueryHyperVMachines()
public List<HyperVGuest> GetHyperVGuests()
{
var hyperVMachines = new List<HyperVGuest>();
var wmiQuery = _wmiv2Namespace
? "SELECT VirtualSystemIdentifier, ElementName FROM Msvm_VirtualSystemSettingData WHERE VirtualSystemType = 'Microsoft:Hyper-V:System:Realized'"
: "SELECT SystemName, ElementName FROM Msvm_VirtualSystemSettingData WHERE SettingType = 3";
? "SELECT * FROM Msvm_VirtualSystemSettingData WHERE VirtualSystemType = 'Microsoft:Hyper-V:System:Realized'"
: "SELECT * FROM Msvm_VirtualSystemSettingData WHERE SettingType = 3";
//Temporary Stringbuilder to build up the VM selectors
var sb = new StringBuilder();
using (var moCollection = new ManagementObjectSearcher(_wmiScope, new ObjectQuery(wmiQuery)).Get())
foreach (var mObject in moCollection)
{
var paths = GetAllVmVhdPaths((string)mObject[_vmIdField]);
paths.AddRange(GetAllVmConfigPaths((string)mObject[_vmIdField]));
if (RequestedVmNames == null || !RequestedVmNames.Any()) return null;
foreach (var vmName in RequestedVmNames)
{
if (sb.Length > 0)
sb.Append(" OR ");
sb.Append(string.Format("ElementName = \'{0}\'", vmName.Replace("'", "''")));
}
wmiQuery += string.Format(" AND ({0})", sb);
hyperVMachines.Add(new HyperVGuest((string)mObject["ElementName"], (string)mObject[_vmIdField], paths));
}
return wmiQuery;
return hyperVMachines;
}
#region Management Object helpers
/// <summary>
/// Executes the WMI Query
/// For given Hyper-V guest it enumerate all associated configuration files
/// </summary>
/// <param name="query"></param>
/// <returns>A collection of results</returns>
private ManagementObjectCollection GetWmiObjects(string query)
/// <returns>A collection of configuration paths</returns>
private List<string> GetAllVmConfigPaths(string vmID)
{
var moSearcher = new ManagementObjectSearcher(_wmiScope, new ObjectQuery(query));
var resultset = moSearcher.Get();
return resultset;
}
var result = new List<string>();
string path;
var wmiQuery = _wmiv2Namespace
? string.Format("select * from Msvm_VirtualSystemSettingData where {0}='{1}'", _vmIdField, vmID)
: string.Format("select * from Msvm_VirtualSystemGlobalSettingData where {0}='{1}'", _vmIdField, vmID);
/// <summary>
/// Queries and gets a Management Object
/// </summary>
/// <param name="className"></param>
/// <param name="where"></param>
/// <returns></returns>
private ManagementObject GetMsVMObject(string className, string where)
{
var query = where != null
? string.Format("select * from {0} where {1}", className, where)
: string.Format("select * from {0}", className);
var resultset = GetWmiObjects(query);
if (resultset.Count != 1)
throw new InvalidOperationException(string.Format("Cannot locate {0} where {1}", className, where));
try
{
foreach (var instance in resultset)
return instance as ManagementObject;
return null;
}
catch (Exception ex)
{
throw new InvalidOperationException(string.Format("Failure retrieving {0} where {1}", className, where), ex);
}
}
private ManagementObject GetTargetVmObject(string vmElementName)
{
var query = string.Format("select * from Msvm_ComputerSystem where ElementName = '{0}'", vmElementName);
var computers = GetWmiObjects(query);
foreach (var instance in computers)
return (ManagementObject)instance;
return null;
}
# endregion Management Object helpers
#region Recovery Virtual Machines
/// <summary>
/// Please refer to https://blogs.msdn.microsoft.com/sergeim/2008/06/03/prepare-vm-create-vm-programmatically-hyper-v-api-c-version/
/// Creation of a Hyper-V Machine
/// </summary>
/// <param name="vmDisplayName"></param>
/// <param name="vmNotes"></param>
public void CreateHyperVMachine(string vmDisplayName, string vmNotes)
{
var sysManService = GetMsVMObject(WmiClasses.MsVM_VSMS, null);
//Defining a VM with empty settings
var hyperVM = sysManService.InvokeMethod(Methods.DefineVirtualSystem,
sysManService.GetMethodParameters(Methods.DefineVirtualSystem), null);
if ((uint) hyperVM["ReturnValue"] != ReturnCode.ERROR_SUCCESS)
throw new InvalidOperationException(
$"DefineVirtualSystem failed. ReturnValue: {(uint) hyperVM["ReturnValue"]}");
var hyperVMTemplate = new ManagementObject((string) hyperVM["DefinedSystem"]);
// this is GUID; will need to locate settings for this VM and edit the settings
var hyperVMSettings = GetMsVMObject(WmiClasses.MsVM_VSSD,
string.Format("systemname = '{0}'", (string) hyperVMTemplate["name"]));
hyperVMSettings["elementname"] = vmDisplayName;
hyperVMSettings["notes"] = vmNotes;
hyperVMSettings["BIOSGUID"] = new Guid();
hyperVMSettings["BIOSNumLock"] = "true";
hyperVMSettings["Description"] = "Hyper-V Machine restored from machine xxxx by Duplicati";
hyperVMSettings.Put();
var hyperVMParams = sysManService.GetMethodParameters(Methods.ModifyVirtualSystem);
var settingsText = hyperVMSettings.GetText(TextFormat.WmiDtd20);
hyperVMParams["ComputerSystem"] = hyperVMTemplate;
hyperVMParams["SystemSettingData"] = settingsText;
sysManService.InvokeMethod(Methods.ModifyVirtualSystem, hyperVMParams, null);
}
#endregion
#region Merging VHD
public void MergeVhd(List<string> VmNames)
{
var vhdPaths = VmNames.SelectMany(x => GetAllVmVhdPaths(x)).ToArray();
var imgMan = GetMsVMObject(WmiClasses.MsVM_IMS, null);
var inParams = imgMan.GetMethodParameters(Methods.MergeVirtualHardDisk);
inParams["DestinationPath"] = vhdPaths.First();
inParams["SourcePath"] = vhdPaths.Last();
var outParams = imgMan.InvokeMethod(Methods.MergeVirtualHardDisk, inParams, null);
if (outParams != null && (uint)outParams["ReturnValue"] == ReturnCode.ERROR_JOBSTARTED)
{
JobCompleted(outParams, _wmiScope);
/*var result = JobCompleted(outParams, _wmiScope)
? $"{inParams["SourcePath"]} was merged successfully."
: $"{inParams["SourcePath"]} failed merging.";
*/
}
//Remove the snapshots since they are no longer valid.
RemoveSnapshotTree();
}
private void RemoveSnapshotTree()
{
GetTargetVmObject("merge");
}
public static bool JobCompleted(ManagementBaseObject outParams, ManagementScope scope)
{
bool jobCompleted = true;
//Retrieve msvc_StorageJob path. This is a full wmi path
var JobPath = (string)outParams["Job"];
var Job = new ManagementObject(scope, new ManagementPath(JobPath), null);
//Try to get storage job information
Job.Get();
while ((UInt16)Job["JobState"] == JobState.Starting
|| (UInt16)Job["JobState"] == JobState.Running)
{
Logging.Log.WriteMessage(string.Format("HyperV in progress... {0}% completed.", Job["PercentComplete"]), Logging.LogMessageType.Information);
System.Threading.Thread.Sleep(1000);
Job.Get();
}
//Figure out if job failed
var jobState = (UInt16)Job["JobState"];
if (jobState != JobState.Completed)
{
var jobErrorCode = (UInt16)Job["ErrorCode"];
Logging.Log.WriteMessage(string.Format("HyperV Error, code: {0}, message: {1}", jobErrorCode, Job["ErrorDescription"]), Logging.LogMessageType.Error);
jobCompleted = false;
}
return jobCompleted;
}
private List<string> GetAllVmVhdPaths(string vmName)
{
var ParentPaths = new List<string>();
var vm = GetTargetVmObject(vmName);
var imgMan = GetMsVMObject(WmiClasses.MsVM_IMS, null);
foreach (var sysSettings in vm.GetRelated(WmiClasses.MsVM_VSSD))
{
var systemObjCollection = ((ManagementObject)sysSettings).GetRelated(WmiClasses.MsVM_RASD);
var tempvhd = from ManagementBaseObject systemBaseObj in systemObjCollection
where ((UInt16) systemBaseObj["ResourceType"] == 21
&& (string) systemBaseObj["ResourceSubType"] == "Microsoft Virtual Hard Disk")
select systemBaseObj;
ParentPaths.Add(((IEnumerable<object>) tempvhd.First()["Connection"]).First().ToString());
}
var result = new List<string>() {ParentPaths.First()};
var inParams = imgMan.GetMethodParameters(Methods.GetVirtualHardDiskInfo);
foreach (var vhdPath in ParentPaths)
{
inParams["Path"] = vhdPath;
var outParams = imgMan.InvokeMethod(Methods.GetVirtualHardDiskInfo, inParams,
null);
if (outParams != null)
using (var mObject1 = new ManagementObjectSearcher(_wmiScope, new ObjectQuery(wmiQuery)).Get().Cast<ManagementObject>().First())
if (_wmiv2Namespace)
{
var doc = new XmlDocument();
doc.LoadXml((string) outParams["Info"]);
var node = doc.SelectSingleNode("//PROPERTY[@NAME = 'ParentPath']/VALUE/child::text()");
if (node == null)
path = Path.Combine((string)mObject1["ConfigurationDataRoot"], (string)mObject1["ConfigurationFile"]);
if (File.Exists(path))
result.Add(path);
using (var snaps = new ManagementObjectSearcher(_wmiScope, new ObjectQuery(string.Format(
"SELECT * FROM Msvm_VirtualSystemSettingData where VirtualSystemType='Microsoft:Hyper-V:Snapshot:Realized' and {0}='{1}'",
_vmIdField, vmID))).Get())
{
if (result.Contains(vhdPath)) continue;
result.Insert(0, vhdPath);
foreach (var snap in snaps)
{
path = Path.Combine((string)snap["ConfigurationDataRoot"], (string)snap["ConfigurationFile"]);
if (File.Exists(path))
result.Add(path);
path = Utility.Utility.AppendDirSeparator(Path.Combine((string)snap["ConfigurationDataRoot"], (string)snap["SuspendDataRoot"]));
if (Directory.Exists(path))
result.Add(path);
}
}
if (!result.Contains(vhdPath))
result.Add(vhdPath);
if (result.Contains(vhdPath))
result.Insert(result.IndexOf(vhdPath), node.Value);
}
}
return result;
else
{
path = Path.Combine((string)mObject1["ExternalDataRoot"], "Virtual Machines", vmID + ".xml");
if (File.Exists(path))
result.Add(path);
path = Utility.Utility.AppendDirSeparator(Path.Combine((string)mObject1["ExternalDataRoot"], "Virtual Machines", vmID));
if (Directory.Exists(path))
result.Add(path);
var snapsIDs = new ManagementObjectSearcher(_wmiScope, new ObjectQuery(string.Format(
"SELECT * FROM Msvm_VirtualSystemSettingData where SettingType=5 and {0}='{1}'",
_vmIdField, vmID))).Get().OfType<ManagementObject>().Select(o => (string)o.GetPropertyValue("InstanceID")).ToList();
foreach (var snapID in snapsIDs)
{
path = Path.Combine((string)mObject1["SnapshotDataRoot"], "Snapshots", snapID.Replace("Microsoft:", "") + ".xml");
if (File.Exists(path))
result.Add(path);
path = Utility.Utility.AppendDirSeparator(Path.Combine((string)mObject1["SnapshotDataRoot"], "Snapshots", snapID.Replace("Microsoft:", "")));
if (Directory.Exists(path))
result.Add(path);
}
}
return result.Distinct(Utility.Utility.ClientFilenameStringComparer).ToList();
}
#endregion
}
}
/// <summary>
/// For given Hyper-V guest it enumerate all associated VHD files
/// </summary>
/// <param name="query"></param>
/// <returns>A collection of VHD paths</returns>
private List<string> GetAllVmVhdPaths(string vmID)
{
var result = new List<string>();
using (var vm = new ManagementObjectSearcher(_wmiScope, new ObjectQuery(string.Format("select * from Msvm_ComputerSystem where Name = '{0}'", vmID)))
.Get().OfType<ManagementObject>().First())
{
foreach (var sysSettings in vm.GetRelated("MsVM_VirtualSystemSettingData"))
using (var systemObjCollection = ((ManagementObject)sysSettings).GetRelated(_wmiv2Namespace ? "MsVM_StorageAllocationSettingData" : "MsVM_ResourceAllocationSettingData"))
{
List<string> tempvhd;
if (_wmiv2Namespace)
tempvhd = (from ManagementBaseObject systemBaseObj in systemObjCollection
where ((UInt16)systemBaseObj["ResourceType"] == 31
&& (string)systemBaseObj["ResourceSubType"] == "Microsoft:Hyper-V:Virtual Hard Disk")
select ((string[])systemBaseObj["HostResource"])[0]).ToList();
else
tempvhd = (from ManagementBaseObject systemBaseObj in systemObjCollection
where ((UInt16)systemBaseObj["ResourceType"] == 21
&& (string)systemBaseObj["ResourceSubType"] == "Microsoft Virtual Hard Disk")
select ((string[])systemBaseObj["Connection"])[0]).ToList();
foreach (var vhd in tempvhd)
result.Add(vhd);
}
}
using (var imgMan = new ManagementObjectSearcher(_wmiScope, new ObjectQuery("select * from MsVM_ImageManagementService")).Get().OfType<ManagementObject>().First())
{
var ParentPaths = new List<string>();
var inParams = imgMan.GetMethodParameters(_wmiv2Namespace ? "GetVirtualHardDiskSettingData" : "GetVirtualHardDiskInfo");
foreach (var vhdPath in result)
{
inParams["Path"] = vhdPath;
using (var outParams = imgMan.InvokeMethod(_wmiv2Namespace ? "GetVirtualHardDiskSettingData" : "GetVirtualHardDiskInfo", inParams, null))
{
if (outParams != null)
{
var doc = new System.Xml.XmlDocument();
doc.LoadXml((string)outParams[_wmiv2Namespace ? "SettingData" : "Info"]);
var node = doc.SelectSingleNode("//PROPERTY[@NAME = 'ParentPath']/VALUE/child::text()");
if (node != null && File.Exists(node.Value))
ParentPaths.Add(node.Value);
}
}
}
result.AddRange(ParentPaths);
}
return result.Distinct(Utility.Utility.ClientFilenameStringComparer).ToList();
}
}
}
+112 -53
View File
@@ -24,6 +24,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using Alphaleonis.Win32.Vss;
using System.Management;
namespace Duplicati.Library.Snapshots
{
@@ -70,10 +71,6 @@ namespace Duplicati.Library.Snapshots
/// The Hyper-V VSS Writer Guid
/// </summary>
private static readonly Guid HyperVWriterGuid = new Guid("66841cd4-6ded-4f4b-8f17-fd23f8ddc3de");
/// <summary>
/// A list off Hyper-V Machines that we will back up
/// </summary>
private readonly IDictionary<string, string> m_hyperVMachines = null;
/// <summary>
/// Constructs a new backup snapshot, using all the required disks
@@ -105,62 +102,42 @@ namespace Duplicati.Library.Snapshots
m_sourcepaths = sourcepaths.Select(x => Directory.Exists(x) ? Utility.Utility.AppendDirSeparator(x) : x).ToList();
var requestedHyperVMs = new List<string>();
if (options.ContainsKey("hyperv-backup-vm"))
requestedHyperVMs = options["hyperv-backup-vm"].Split(';').Where(x => !string.IsNullOrWhiteSpace(x) && x.Trim().Length > 0).ToList();
//Check if we are backing up HyperV machines
if (requestedHyperVMs.Count > 0)
{
var hyperVUtility = new HyperVUtility(requestedHyperVMs);
m_hyperVMachines = hyperVUtility.GetHyperVMachines();
#region Testing Features
if (!Utility.Utility.ParseBoolOption(options, "hyperv-backup-no-merge"))
hyperVUtility.MergeVhd(requestedHyperVMs);
//Option: Create-new-vm-after-restore,
//hyperVUtility.CreateHyperVMachine("TestVM", xmlpath);
#endregion Testing Features
}
m_backup.StartSnapshotSet();
List<string> hypervPaths;
try
{
//Gather information on all Vss writers
m_backup.GatherWriterMetadata();
//Update the sourcepaths if we're backing up Hyper-V Machines
if (requestedHyperVMs.Count > 0)
{
//Find sourcepaths in Hyper-V WriterMetaData
var m_backup_wmd = m_backup.WriterMetadata.FirstOrDefault(o => o.WriterId.Equals(HyperVWriterGuid));
if (m_backup_wmd != null)
foreach (var component in m_backup_wmd.Components)
{
//Cross reference the requested Hyper-V Machines to backup and add the sources
if (!m_hyperVMachines.ContainsKey(component.ComponentName))
continue;
foreach (var file in component.Files)
{
if (file.FileSpecification.Contains("*"))
continue;
m_sourcepaths.Add(file.Path + file.FileSpecification);
}
}
}
hypervPaths = PrepareHyperVBackup(options, m_backup.WriterMetadata.FirstOrDefault(o => o.WriterId.Equals(HyperVWriterGuid)));
}
finally
{
m_backup.FreeWriterMetadata();
}
catch
{
try { m_backup.FreeWriterMetadata(); }
catch { }
throw;
}
if (hypervPaths != null)
m_sourcepaths.AddRange(hypervPaths);
//Sanity check for duplicate files/folders
var pathDuplicates = m_sourcepaths.GroupBy(x => x, Utility.Utility.ClientFilenameStringComparer)
.Where(g => g.Count() > 1).Select(y => y.Key).ToList();
foreach(var pathDuplicate in pathDuplicates)
Logging.Log.WriteMessage(string.Format("Removing duplicate source: {0}", pathDuplicate), Logging.LogMessageType.Information);
if(pathDuplicates.Count > 0)
m_sourcepaths = m_sourcepaths.Distinct(Utility.Utility.ClientFilenameStringComparer).OrderBy(a => a).ToList();
//Sanity check for multiple inclusions of the same files/folders
var pathIncludedPaths = m_sourcepaths.Where(x => m_sourcepaths.Where(y => y != x).Any(z => x.StartsWith(z, Utility.Utility.ClientFilenameStringComparision))).ToList();
foreach (var pathIncluded in pathIncludedPaths)
Logging.Log.WriteMessage(string.Format("Removing already included source: {0}", pathIncluded), Logging.LogMessageType.Information);
if (pathIncludedPaths.Count > 0)
m_sourcepaths = m_sourcepaths.Except(pathIncludedPaths, Utility.Utility.ClientFilenameStringComparer).ToList();
m_backup.StartSnapshotSet();
//Figure out which volumes are in the set
m_volumes = new Dictionary<string, Guid>(StringComparer.InvariantCultureIgnoreCase);
@@ -216,6 +193,88 @@ namespace Duplicati.Library.Snapshots
}
}
private List<string> PrepareHyperVBackup(Dictionary<string, string> options, IVssExamineWriterMetadata writerMetaData)
{
var resultPaths = new List<string>();
var requestedHyperVMs = new List<string>();
if (options.ContainsKey("hyperv-backup-vm"))
requestedHyperVMs = options["hyperv-backup-vm"].Split(';').Where(x => !string.IsNullOrWhiteSpace(x) && x.Trim().Length > 0).ToList();
if (requestedHyperVMs.Count == 0)
return resultPaths;
Logging.Log.WriteMessage("Starting to gather Hyper-V information.", Logging.LogMessageType.Information);
var hyperVGuests = new HyperVUtility().GetHyperVGuests();
Logging.Log.WriteMessage(string.Format("Found {0} virtual machines on Hyper-V.", hyperVGuests.Count), Logging.LogMessageType.Information);
bool bNotFound = false;
var notFoundVM = new List<string>();
foreach (var requestedHyperVM in requestedHyperVMs)
{
var foundVMs = hyperVGuests.FindAll(x => (string.Equals(requestedHyperVM, x.ID, StringComparison.CurrentCultureIgnoreCase)));
if (foundVMs.Count != 1)
{
bNotFound = true;
notFoundVM.Add(requestedHyperVM);
Logging.Log.WriteMessage(string.Format("Cannot find virtual machine with ID {0} on Hyper-V.", requestedHyperVM), Logging.LogMessageType.Error);
}
else
Logging.Log.WriteMessage(string.Format("Found virtual machine {0} with ID {1} on Hyper-V.", foundVMs[0].Name, requestedHyperVM), Logging.LogMessageType.Information);
}
if (bNotFound)
throw new Exception(string.Format("Cannot find virtual machine with ID {0} on Hyper-V.", string.Join(", ", notFoundVM.ToArray())));
var productType = new ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem")
.Get().OfType<ManagementObject>()
.Select(o => (uint)o.GetPropertyValue("ProductType")).First();
if (productType != 1) // Hyper-V writer is present only on Server version of Windows
{
if (writerMetaData == null)
throw new Exception("Microsoft Hyper-V VSS Writer not found - cannot backup Hyper-V machines.");
foreach (var component in writerMetaData.Components)
if (requestedHyperVMs.Contains(component.ComponentName, StringComparer.CurrentCultureIgnoreCase))
foreach (var file in component.Files)
if (file.FileSpecification.Contains("*"))
{
if (Directory.Exists(Utility.Utility.AppendDirSeparator(file.Path)))
{
resultPaths.Add(Utility.Utility.AppendDirSeparator(file.Path));
Logging.Log.WriteMessage(string.Format("For VM {0} - adding {1}.", component.ComponentName, Utility.Utility.AppendDirSeparator(file.Path)), Logging.LogMessageType.Profiling);
}
}
else
{
if (File.Exists(Path.Combine(file.Path, file.FileSpecification)))
{
resultPaths.Add(Path.Combine(file.Path, file.FileSpecification));
Logging.Log.WriteMessage(string.Format("For VM {0} - adding {1}.", component.ComponentName, Path.Combine(file.Path, file.FileSpecification)), Logging.LogMessageType.Profiling);
}
}
}
else
{
Logging.Log.WriteMessage("This is client version of Windows. Hyper-V VSS writer is present only on Server version. Backup will continue, but will be crash consistent only in opposite to application consistent in Server version.", Logging.LogMessageType.Warning);
foreach (var hyperVGuest in hyperVGuests)
if (requestedHyperVMs.Contains(hyperVGuest.ID, StringComparer.CurrentCultureIgnoreCase))
foreach (var path in hyperVGuest.DataPaths)
{
resultPaths.Add(path);
Logging.Log.WriteMessage(string.Format("For VM {0} - adding {1}.", hyperVGuest.ID, path), Logging.LogMessageType.Profiling);
}
}
return resultPaths.Distinct(Utility.Utility.ClientFilenameStringComparer).OrderBy(a => a).ToList();
}
#if DEBUG
/// <summary>
/// Returns all files found in the shadow copies
@@ -502,4 +561,4 @@ namespace Duplicati.Library.Snapshots
#endregion
}
}
}
@@ -344,6 +344,18 @@
<Link>licenses\BouncyCastle\licensedata.json</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\thirdparty\angular-gettext\Homepage.txt">
<Link>licenses\AngularGettext\Homepage.txt</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\thirdparty\angular-gettext\License.txt">
<Link>licenses\AngularGettext\License.txt</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\thirdparty\angular-gettext\licensedata.json">
<Link>licenses\AngularGettext\licensedata.json</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
@@ -374,5 +386,6 @@
<Folder Include="licenses\System.Net.FtpClient\" />
<Folder Include="licenses\MimeKit\" />
<Folder Include="licenses\BouncyCastle\" />
<Folder Include="licenses\AngularGettext\" />
</ItemGroup>
</Project>
+11 -30
View File
@@ -1,32 +1,13 @@
We would like to thank all contributors who made Duplicati what it is today.
Software Development:
Livio Bertacco
Daniel Halan
Birunthan Mohanathas
James Colton
Nicolas Hatier, Niversoft idées logicielles
Tobias Käes
Don Lockhart
Sebastian Fahrenholz
Malte Neumann
Mostafa Aghajani
Stefan Lück
Tim Mylemans
Xelio Cheong
Michal Naiman
We would like to thank all contributors who made Duplicati what it is today.
These are the contributors to Duplicati 1.x that got it all started.
These are the people who help others to get their backups up and running.
These are the testers and translators.
These are the people who give us hints how to improve Duplicati further.
These are the people who just say "thank you".
The theme for Duplicati (ngax):
Alexander Franzelin
Translations:
Brazilian, pt-BR: Gabriell Hahmann
Chinese, zh-CN: Born Kanas
Danish, da-DK: Kenneth Skovhede
French, fr-FR: Matthieu Weber
German, de-DE: Ernst Mair, Mathias Block, DooM ElHado, Leo nowhere
Italian, it-IT: Riccardo Stoppani
Russian, ru-RU: Philip Torchinsky, Semonix
Spanish, es-ES: JosepV
You are the reason why this project gets ahead. You do not only contribute to the software but also keep us involved and motivated. Thank you!
A special thanks to the many users who let us know how they are using Duplicati, ask for new features, test development builds and thus help us improve Duplicati further.
Due to their recent contributions to Duplicati 2.0 we would like to thank some people in particular:
Stefan Lück (@footstark) for providing great performance optimizations to Duplicati 2.0
Tobias Käs (@weltkante) for his C# library that implements LZMA2 and 7z
Alexander Franzelin (@AlexFRAN) for the design of the Duplicati UI and the website
Binary file not shown.
@@ -1,17 +0,0 @@
To remove the heavy load on SVN with the many translated files,
the system is now changed to rely on a single CSV file pr. translation.
To work with it, use the CSV file, either here on from google docs:
https://docs.google.com/leaf?id=0B9tKh3OEoqEqOTZmNWI1NWEtOTZkNi00OTk4LTkzMWEtOGI1M2UzMTc3NWYy
To generate the structure needed to build the translation, run:
LocalizationTool.exe import fr-FR report.fr-FR.csv
This will generate the fr-FR folder, in which you can
perform a UI customization (move buttons etc).
Once done, export the changes back to the CSV file:
LocalizationTool.exe export fr-FR
To build the assemblies run:
LocalizationTool.exe build
-62
View File
@@ -1,62 +0,0 @@
<root>
<configuration name="Duplicati">
<assembly name="Duplicati" folder="GUI" namespace="Duplicati.GUI">
<exclude>Datamodel</exclude>
</assembly>
<assembly name="Duplicati.Datamodel" folder="GUI\Datamodel" />
<assembly name="Duplicati.CommandLine" folder="CommandLine">
<exclude>Decrypter</exclude>
</assembly>
<assembly name="Duplicati.CommandLine.Decrypter" folder="CommandLine\Decrypter" />
<assembly name="Duplicati.Library.Utility" folder="Library\Utility" />
<assembly name="Duplicati.Library.Main" folder="Library\Main" />
<assembly name="Duplicati.Library.Encryption" folder="Library\Encryption" />
<assembly name="Duplicati.Library.SharpExpect" folder="Library\SharpExpect" />
<assembly name="Duplicati.Library.Compression" folder="Library\Compression" />
<assembly name="Duplicati.Library.Encryption" folder="Library\Encryption" />
<assembly name="Duplicati.Library.SharpRSync" folder="Library\SharpRSync" />
<assembly name="Duplicati.Library.Shapshots" folder="Library\Snapshots" />
<assembly name="Duplicati.Library.Interface" folder="Library\Interface" />
<assembly name="Duplicati.Library.DynamicLoader" folder="Library\DynamicLoader" />
<assembly name="Duplicati.Library.Modules.Builtin" folder="Library\Modules\Builtin" />
<assembly name="Duplicati.Library.Backend.FTP" folder="Library\Backend\FTP" namespace="Duplicati.Library.Backend" />
<assembly name="Duplicati.Library.Backend.File" folder="Library\Backend\File" namespace="Duplicati.Library.Backend" />
<assembly name="Duplicati.Library.Backend.S3" folder="Library\Backend\S3" namespace="Duplicati.Library.Backend" />
<assembly name="Duplicati.Library.Backend.WEBDAV" folder="Library\Backend\WEBDAV" namespace="Duplicati.Library.Backend" />
<assembly name="Duplicati.Library.Backend.CloudFiles" folder="Library\Backend\CloudFiles" namespace="Duplicati.Library.Backend" />
<assembly name="Duplicati.Library.Backend.SSHv2" folder="Library\Backend\SSHv2" namespace="Duplicati.Library.Backend" />
<assembly name="Duplicati.Winforms.Controls" folder="Winforms\Controls" namespace="Duplicati.Winforms.Controls" />
<keyfile>..\GUI\Duplicati.snk</keyfile>
<versionassembly>..\GUI\bin\Release\Duplicati.exe</versionassembly>
<sourcefolder>..</sourcefolder>
<outputfolder>compiled</outputfolder>
<productname>Duplicati</productname>
</configuration>
<!-- We need these without a keyfile, because they depend on unsigned dll's -->
<configuration name="SSH">
<assembly name="Duplicati.Library.Backend.TahoeLAFS" folder="Library\Backend\TahoeLAFS" namespace="Duplicati.Library.Backend" />
<versionassembly>..\GUI\bin\Release\Duplicati.exe</versionassembly>
<sourcefolder>..</sourcefolder>
<outputfolder>compiled</outputfolder>
<productname>Duplicati</productname>
</configuration>
<configuration name="WizardForm">
<assembly name="System.Windows.Forms.Wizard" folder="WizardForm" />
<keyfile>..\..\WizardForm\WizardForm.snk</keyfile>
<versionassembly>..\GUI\bin\Release\System.Windows.Forms.Wizard.dll</versionassembly>
<sourcefolder>..\..</sourcefolder>
<outputfolder>compiled</outputfolder>
<productname>WizardForm</productname>
</configuration>
</root>
-44
View File
@@ -1,44 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<ignore>OK</ignore>
<ignore>Pause</ignore>
<ignore>Start</ignore>
<ignore>Stop</ignore>
<ignore>Log</ignore>
<ignore>Log data</ignore>
<ignore>Status</ignore>
<ignore>Destination</ignore>
<ignore>Pause backup</ignore>
<ignore>Stop backup</ignore>
<ignore>Duplicati status</ignore>
<ignore>s</ignore>
<ignore>Filter</ignore>
<ignore>gb</ignore>
<ignore>gb/s</ignore>
<ignore>mb</ignore>
<ignore>mb/s</ignore>
<ignore>kb</ignore>
<ignore>kb/s</ignore>
<ignore>...</ignore>
<ignore>(...)</ignore>
<ignore>Normal</ignore>
<ignore>backup</ignore>
<ignore>--{0}: {1}</ignore>
<ignore>Backup: {0}</ignore>
<ignore>Interval</ignore>
<ignore>Server</ignore>
<ignore>Get start</ignore>
<ignore>Put start</ignore>
<ignore>List start</ignore>
<ignore>AWS Access ID</ignore>
<ignore>AWS Secret Key</ignore>
<ignore>CloudFiles API Key</ignore>
<ignore>{0:N} GB</ignore>
<ignore>{0:N} KB</ignore>
<ignore>{0:N} MB</ignore>
<ignore>CloudFiles</ignore>
<ignore>{0}\t{1}</ignore>
<ignore>argument</ignore>
<ignore>Dialog</ignore>
<ignore>{0}</ignore>
</root>
-40
View File
@@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<ignore> Inc\t{0}\t{1}\t{2}</ignore>
<ignore>(...)</ignore>
<ignore>--{0}: {1}</ignore>
<ignore>...</ignore>
<ignore>1 Minute</ignore>
<ignore>AWS Access ID</ignore>
<ignore>Argument</ignore>
<ignore>CloudFiles (Rackspace)</ignore>
<ignore>CloudFiles AuthUrl</ignore>
<ignore>Dialog</ignore>
<ignore>Duplicati Setup, Version {0}</ignore>
<ignore>Duplicati {0} - Status</ignore>
<ignore>Filter</ignore>
<ignore>Gb</ignore>
<ignore>Gb/s</ignore>
<ignore>Homepage</ignore>
<ignore>MB</ignore>
<ignore>MB/s</ignore>
<ignore>Name</ignore>
<ignore>Normal</ignore>
<ignore>OK</ignore>
<ignore>Pause</ignore>
<ignore>Port (optional)</ignore>
<ignore>Server</ignore>
<ignore>Status</ignore>
<ignore>Stop</ignore>
<ignore>bytes</ignore>
<ignore>bytes/s</ignore>
<ignore>kb</ignore>
<ignore>kb/s</ignore>
<ignore>s</ignore>
<ignore>{0:N} GB</ignore>
<ignore>{0:N} MB</ignore>
<ignore>{0:N} kB</ignore>
<ignore>{0}</ignore>
<ignore>{0} bytes</ignore>
<ignore>{0}\t{1}</ignore>
</root>
-25
View File
@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<ignore>OK</ignore>
<ignore>...</ignore>
<ignore>(...)</ignore>
<ignore> Inc\t{0}\t{1}\t{2}</ignore>
<ignore>--{0}: {1}</ignore>
<ignore>1 minute</ignore>
<ignore>15 minutes</ignore>
<ignore>30 minutes</ignore>
<ignore>5 minutes</ignore>
<ignore>Action</ignore>
<ignore>CloudFiles</ignore>
<ignore>Destination</ignore>
<ignore>Normal</ignore>
<ignore>Options...</ignore>
<ignore>Pause</ignore>
<ignore>Simple &lt;&lt;&lt;</ignore>
<ignore>Source</ignore>
<ignore>Stop</ignore>
<ignore>argument</ignore>
<ignore>s</ignore>
<ignore>{0}</ignore>
<ignore>{0}\t{1}</ignore>
</root>
-29
View File
@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<ignore>{0}\t{1}</ignore>
<ignore>Password</ignore>
<ignore>OK</ignore>
<ignore>...</ignore>
<ignore>s</ignore>
<ignore>(...)</ignore>
<ignore>bytes</ignore>
<ignore>bytes/s</ignore>
<ignore>gb</ignore>
<ignore>gb/s</ignore>
<ignore>kb</ignore>
<ignore>kb/s</ignore>
<ignore>mb</ignore>
<ignore>mb/s</ignore>
<ignore>Stop</ignore>
<ignore>backup</ignore>
<ignore>{0}</ignore>
<ignore>--{0}:{1}</ignore>
<ignore>Backup: {0}</ignore>
<ignore>CloudFiles (Rackspace)</ignore>
<ignore>Server</ignore>
<ignore>keyfile</ignore>
<ignore>{0} bytes</ignore>
<ignore>{0:N} GB</ignore>
<ignore>{0:N} KB</ignore>
<ignore>{0:N} MB</ignore>
</root>
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1 +0,0 @@
https://docs.google.com/spreadsheet/pub?key=0AttKh3OEoqEqdC1EYmdEaFZwM3RfUy1fRFpzQ3VHeGc&single=true&gid=0&output=csv
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1 +0,0 @@
https://docs.google.com/spreadsheet/pub?key=0AttKh3OEoqEqdHNRWTVqd2pFaWZMQ3RNTXRvclhVZHc&single=true&gid=0&output=csv
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1 +0,0 @@
https://docs.google.com/spreadsheet/pub?key=0AttKh3OEoqEqdHVnOVlRV01aQ3o0OV9YMVdHNzJVb2c&single=true&gid=0&output=csv
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1 +0,0 @@
https://docs.google.com/spreadsheet/pub?key=0AttKh3OEoqEqdDE2dHhsNU0tNlJLUnpBRzVVRVhjbWc&single=true&gid=5&output=csv
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1 +0,0 @@
https://docs.google.com/spreadsheet/pub?key=0AttKh3OEoqEqdHFZTXdUcHJ0M0tWc3BSWE1BejhRZUE&single=true&gid=0&output=csv
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1 +0,0 @@
https://docs.google.com/spreadsheet/pub?key=0AilOWz75w8nHdGdFaEJlR3BGN0t5VlQyWlNhNlhvckE&single=true&gid=0&output=csv
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1 +0,0 @@
https://docs.google.com/spreadsheet/pub?key=0AttKh3OEoqEqdFRDQ1hZaUhjai1feEVSeTVkNUdCVlE&single=true&gid=0&output=csv

Some files were not shown because too many files have changed in this diff Show More