using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; using Amazon.Runtime.Internal; using BTCPayServer.Data; using Microsoft.Extensions.Configuration; using Microsoft.WindowsAzure.Storage.RetryPolicies; using NBitcoin.DataEncoders; using Newtonsoft.Json.Linq; using Xunit; namespace BTCPayServer.Tests { /// /// This class hold easy to run utilities for dev time /// public class UtilitiesTests { /// /// Download transifex transactions and put them in BTCPayServer\wwwroot\locales /// [FactWithSecret("TransifexAPIToken")] [Trait("Utilities", "Utilities")] public async Task PullTransifexTranslations() { // 1. Generate an API Token on https://www.transifex.com/user/settings/api/ // 2. Run "dotnet user-secrets set TransifexAPIToken " var client = new TransifexClient(FactWithSecretAttribute.GetFromSecrets("TransifexAPIToken")); var proj = "o:btcpayserver:p:btcpayserver"; var resource = $"{proj}:r:enjson"; var json = await client.GetTransifexAsync($"https://rest.api.transifex.com/resource_language_stats?filter[project]={proj}&filter[resource]={resource}"); var langs = json["data"].Select(o => o["id"].Value().Split(':').Last()).ToArray(); json = await client.GetTransifexAsync($"https://rest.api.transifex.com/resource_strings?filter[resource]={resource}"); var hashToKeys = json["data"] .ToDictionary( o => o["id"].Value().Split(':').Last(), o => o["attributes"]["key"].Value().Replace("\\.", ".")); var translations = new ConcurrentDictionary(); translations.TryAdd("en", new JObject( json["data"] .Select(o => new JProperty( o["attributes"]["key"].Value().Replace("\\.", "."), o["attributes"]["strings"]["other"].Value()))) ); var langsDir = Path.Combine(TestUtils.TryGetSolutionDirectoryInfo().FullName, "BTCPayServer", "wwwroot", "locales"); Task.WaitAll(langs.Select(async l => { if (l == "en") return; retry: var j = await client.GetTransifexAsync($"https://rest.api.transifex.com/resource_translations?filter[resource]={resource}&filter[language]=l:{l}"); try { var jobj = new JObject( j["data"].Select(o => (Key: hashToKeys[o["id"].Value().Split(':')[^3]], Strings: o["attributes"]["strings"])) .Select(o => new JProperty( o.Key, o.Strings.Type == JTokenType.Null ? translations["en"][o.Key].Value() : o.Strings["other"].Value() ))); if (l == "ne_NP") l = "np_NP"; if (l == "zh_CN") l = "zh-SP"; if (l == "kk") l = "kk-KZ"; var langCode = l.Replace("_", "-"); jobj["code"] = langCode; if ((string)jobj["currentLanguage"] == "English") return; // Not translated if ((string)jobj["currentLanguage"] == "disable") return; // Not translated if (jobj["InvoiceExpired_Body_3"].Value() == translations["en"]["InvoiceExpired_Body_3"].Value()) { jobj["InvoiceExpired_Body_3"] = string.Empty; } translations.TryAdd(langCode, jobj); } catch { goto retry; } }).ToArray()); foreach (var t in translations) { t.Value.AddFirst(new JProperty("NOTICE_WARN", "THIS CODE HAS BEEN AUTOMATICALLY GENERATED FROM TRANSIFEX, IF YOU WISH TO HELP TRANSLATION COME ON THE SLACK http://slack.btcpayserver.org TO REQUEST PERMISSION TO https://www.transifex.com/btcpayserver/btcpayserver/")); var content = t.Value.ToString(Newtonsoft.Json.Formatting.Indented); File.WriteAllText(Path.Combine(langsDir, $"{t.Key}.json"), content); } } } public class TransifexClient { public TransifexClient(string apiToken) { Client = new HttpClient(); APIToken = apiToken; } public HttpClient Client { get; } public string APIToken { get; } public async Task GetTransifexAsync(string uri) { var message = new HttpRequestMessage(HttpMethod.Get, uri); message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", APIToken); message.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/vnd.api+json")); var response = await Client.SendAsync(message); var str = await response.Content.ReadAsStringAsync(); return JObject.Parse(str); } } }