11using System ;
22using System . Collections . Generic ;
33using System . IO ;
4+ using System . IO . Compression ;
45using System . Linq ;
6+ using System . Net ;
57using System . Windows ;
68using System . Xml ;
9+ using Octokit ;
710using SPCode . Utils ;
8- using static SPCode . Utils . DefaultTranslations ;
911
1012namespace SPCode . Interop
1113{
1214 public class TranslationProvider
1315 {
14- public string [ ] AvailableLanguageIDs ;
15- public string [ ] AvailableLanguages ;
16-
16+ public List < string > AvailableLanguageIDs = new ( ) ;
17+ public List < string > AvailableLanguages = new ( ) ;
1718 public bool IsDefault = true ;
1819
19- private readonly Dictionary < string , string > LangDict = new ( StringComparer . OrdinalIgnoreCase ) ;
20+ private readonly string _tempDir = Paths . GetTempDirectory ( ) ;
21+ private readonly string _translationsDir = Paths . GetTranslationsDirectory ( ) ;
22+ private static readonly Dictionary < string , string > _langDictionary = new ( StringComparer . OrdinalIgnoreCase ) ;
23+ private Release _latestVersion ;
24+
25+ public TranslationProvider ( )
26+ {
27+ // Make sure translations dir exists
28+ if ( ! Directory . Exists ( _translationsDir ) )
29+ {
30+ Directory . CreateDirectory ( _translationsDir ) ;
31+ }
32+
33+ if ( IsUpdateAvailable ( ) )
34+ {
35+ UpdateTranslations ( ) ;
36+ }
37+
38+ ParseTranslationFiles ( ) ;
39+ }
2040
2141 /// <summary>
2242 /// Gets the translation of the specified phrase.
2343 /// </summary>
2444 /// <param name="phrase">The phrase to return translated</param>
2545 /// <returns></returns>
26- public string Get ( string phrase )
46+ public static string Translate ( string phrase )
2747 {
28- return LangDict . ContainsKey ( phrase ) ? LangDict [ phrase ] : "<empty>" ;
48+ return _langDictionary . ContainsKey ( phrase ) ? _langDictionary [ phrase ] : "<empty>" ;
2949 }
3050
3151 /// <summary>
3252 /// Loads the specified language.
3353 /// </summary>
3454 /// <param name="lang">The language to load</param>
35- /// <param name="Initial"> </param>
36- public void LoadLanguage ( string lang , bool Initial = false )
55+ /// <param name="initial">Whether this was the startup initial method call </param>
56+ public void LoadLanguage ( string lang , bool initial = false )
3757 {
38- DefaultLangDict . Keys . ToList ( ) . ForEach ( x => LangDict [ x ] = DefaultLangDict [ x ] ) ;
39- var languageList = new List < string > ( ) ;
40- var languageIDList = new List < string > ( ) ;
41- languageList . Add ( "English" ) ;
42- languageIDList . Add ( "" ) ;
58+ // This is probably the first boot ever
59+ if ( lang == string . Empty )
60+ {
61+ lang = Constants . DefaultLanguageID ;
62+ Program . OptionsObject . Language = lang ;
63+ }
4364 lang = lang . Trim ( ) . ToLowerInvariant ( ) ;
44- IsDefault = ( string . IsNullOrEmpty ( lang ) || lang . ToLowerInvariant ( ) == "en" ) && Initial ;
45- if ( File . Exists ( Constants . LanguagesFile ) )
65+ IsDefault = ( string . IsNullOrEmpty ( lang ) || lang . ToLowerInvariant ( ) == Constants . DefaultLanguageID ) && initial ;
66+ var doc = new XmlDocument ( ) ;
67+
68+ try
4669 {
47- try
70+ // Fill with defaults first
71+ if ( initial )
4872 {
49- var document = new XmlDocument ( ) ;
50- document . Load ( Constants . LanguagesFile ) ;
51- if ( document . ChildNodes . Count < 1 )
73+ doc . Load ( Path . Combine ( _translationsDir , Constants . DefaultTranslationsFile ) ) ;
74+ foreach ( XmlNode node in doc . ChildNodes [ 0 ] . ChildNodes )
5275 {
53- throw new Exception ( "No Root-Node: \" translations \" found" ) ;
76+ _langDictionary . Add ( node . Name , node . InnerText ) ;
5477 }
5578
56- XmlNode rootLangNode = null ;
57- foreach ( XmlNode childNode in document . ChildNodes [ 0 ] . ChildNodes )
79+ // Return if the attempted language to load is the default one
80+ if ( lang == Constants . DefaultLanguageID )
5881 {
59- var lID = childNode . Name ;
60- var lNm = lID ;
61- if ( childNode . Name . ToLowerInvariant ( ) == lang )
62- {
63- rootLangNode = childNode ;
64- }
65- if ( childNode . FirstChild . Name . ToLowerInvariant ( ) == "language" )
66- {
67- lNm = childNode . FirstChild . InnerText ;
68- }
69- languageList . Add ( lNm ) ;
70- languageIDList . Add ( lID ) ;
82+ return ;
7183 }
72- if ( rootLangNode != null )
84+ }
85+
86+ var file = Path . Combine ( _translationsDir , $ "{ lang } .xml") ;
87+ if ( ! File . Exists ( file ) )
88+ {
89+ UpdateTranslations ( ) ;
90+ LoadLanguage ( lang ) ;
91+ return ;
92+ }
93+ else
94+ {
95+ doc . Load ( file ) ;
96+
97+ // Replace existing keys with the ones available in this file
98+ foreach ( XmlNode node in doc . ChildNodes [ 0 ] . ChildNodes )
7399 {
74- foreach ( XmlNode node in rootLangNode . ChildNodes )
75- {
76- if ( node . NodeType == XmlNodeType . Comment )
77- {
78- continue ;
79- }
80- var nn = node . Name . ToLowerInvariant ( ) ;
81- var nv = node . InnerText ;
82- LangDict [ nn ] = nv ;
83- }
100+ _langDictionary [ node . Name ] = node . InnerText ;
84101 }
85102 }
86- catch ( Exception e )
103+ }
104+ catch ( Exception )
105+ {
106+ throw ;
107+ }
108+ }
109+
110+ /// <summary>
111+ /// Gets all of the translation files and adds them to the global available languages list.
112+ /// </summary>
113+ public void ParseTranslationFiles ( )
114+ {
115+ try
116+ {
117+ var filesDir = Directory . GetFiles ( _translationsDir ) . Where ( x => x . EndsWith ( ".xml" ) ) ;
118+ foreach ( var file in filesDir )
119+ {
120+ // Create wrapper
121+ var fInfo = new FileInfo ( file ) ;
122+
123+ // Parse content in an XML object
124+ var doc = new XmlDocument ( ) ;
125+ doc . LoadXml ( File . ReadAllText ( fInfo . FullName ) ) ;
126+
127+ // Get language name and ID
128+ var langName = doc . ChildNodes [ 0 ] . ChildNodes
129+ . Cast < XmlNode > ( )
130+ . Single ( x => x . Name == "language" )
131+ . InnerText ;
132+ var langID = fInfo . Name . Substring ( 0 , fInfo . Name . IndexOf ( '.' ) ) ;
133+
134+ // Add file to the available languages lists
135+ AvailableLanguages . Add ( langName ) ;
136+ AvailableLanguageIDs . Add ( langID ) ;
137+ }
138+ }
139+ catch ( Exception ex )
140+ {
141+ MessageBox . Show ( $ "There was a problem while updating the translations file.\n " +
142+ $ "Details: { ex . Message } ") ;
143+ }
144+ }
145+
146+ /// <summary>
147+ /// Downloads the latest translation files release from GitHub for SPCode to parse them.
148+ /// </summary>
149+ public void UpdateTranslations ( )
150+ {
151+ // Clear temp folder before beggining
152+ DirUtils . ClearTempFolder ( ) ;
153+
154+ // Download latest release zip file
155+ var wc = new WebClient ( ) ;
156+ var downloadedFile = Path . Combine ( _tempDir , "langs.zip" ) ;
157+ wc . Headers . Add ( HttpRequestHeader . UserAgent , Constants . ProductHeaderValueName ) ;
158+ wc . DownloadFile ( _latestVersion . ZipballUrl , downloadedFile ) ;
159+
160+ // Decompress and replace all of its files
161+ ZipFile . ExtractToDirectory ( downloadedFile , _tempDir ) ;
162+ var filesDir = Directory . GetFiles ( Directory . GetDirectories ( _tempDir ) [ 0 ] ) . Where ( x => x . EndsWith ( ".xml" ) ) ;
163+ foreach ( var file in filesDir )
164+ {
165+ // Create wrapper
166+ var fInfo = new FileInfo ( file ) ;
167+
168+ // Replace current file with this one
169+ var destination = Path . Combine ( _translationsDir , fInfo . Name ) ;
170+ if ( File . Exists ( destination ) )
87171 {
88- MessageBox . Show ( "An error occured while reading the language-file. Without them, the editor wont show translations." + Environment . NewLine + "Details: " + e . Message
89- , "Error while reading configs."
90- , MessageBoxButton . OK
91- , MessageBoxImage . Warning ) ;
172+ File . Delete ( destination ) ;
92173 }
174+ File . Move ( fInfo . FullName , destination ) ;
93175 }
94- AvailableLanguages = languageList . ToArray ( ) ;
95- AvailableLanguageIDs = languageIDList . ToArray ( ) ;
176+
177+ // Delete all temp folder contents
178+ DirUtils . ClearTempFolder ( ) ;
179+
180+ // Update version to options object
181+ Program . OptionsObject . TranslationsVersion = int . Parse ( _latestVersion . Name ) ;
182+ }
183+
184+ /// <summary>
185+ /// Compares the stored version of the translations release with the one from GitHub
186+ /// </summary>
187+ /// <returns>Whether there's an update available</returns>
188+ public bool IsUpdateAvailable ( )
189+ {
190+ var client = new GitHubClient ( new ProductHeaderValue ( Constants . ProductHeaderValueName ) ) ;
191+ var versionStored = Program . OptionsObject . TranslationsVersion ;
192+
193+ _latestVersion = client . Repository . Release . GetAll ( Constants . OrgName ,
194+ Constants . TranslationsRepoName ) . Result [ 0 ] ;
195+
196+ return versionStored < int . Parse ( _latestVersion . Name ) ;
96197 }
97198 }
98199}
0 commit comments