public void SubTagComponentDescription_HasForwardSlash_ReplacesWithPipe() { Assert.AreEqual( "=|Kx'au||'ein", StandardSubtags.SubTagComponentDescription("Description: =/Kx'au//'ein") ); }
public void SubTagComponentDescription_HasIndividualLanguage_RemovesIt() { Assert.AreEqual( "Malay", StandardSubtags.SubTagComponentDescription("Description: Malay (individual language)") ); }
public void ConvertToPalasoConformPrivateUseRfc5646Tag(string flexConformPrivateUseRfc5646Tag) { string language = String.Empty; string script = String.Empty; string region = String.Empty; string variant = String.Empty; string privateUse = String.Empty; var tokens = flexConformPrivateUseRfc5646Tag.Split(new[] { '-' }); for (int position = 0; position < tokens.Length; ++position) { string currentToken = tokens[position]; if (position == 0) { if (!currentToken.Equals("x", StringComparison.OrdinalIgnoreCase)) { throw new ValidationException(String.Format("The rfctag {0} does not start with 'x-' or 'X-'.", flexConformPrivateUseRfc5646Tag)); } language = currentToken; } else if (position == 1 && !StandardSubtags.IsValidIso15924ScriptCode(currentToken)) { language = language + '-' + currentToken; } else if (StandardSubtags.IsValidIso15924ScriptCode(currentToken)) { if (!String.IsNullOrEmpty(region) || !String.IsNullOrEmpty(variant)) { throw new ValidationException( String.Format( "The rfctag '{0}' contains a misplaced Script subtag (i.e. it was preceded by a region or variant subtag.", flexConformPrivateUseRfc5646Tag)); } script = currentToken; } else if (StandardSubtags.IsValidIso3166RegionCode(currentToken)) { if (!String.IsNullOrEmpty(variant)) { throw new ValidationException( String.Format( "The rfctag '{0}' contains a misplaced Region subtag (i.e. it was preceded by a variant subtag.", flexConformPrivateUseRfc5646Tag)); } region = currentToken; } else if (StandardSubtags.IsValidRegisteredVariantCode(currentToken)) { variant = variant + currentToken; } else { privateUse = String.IsNullOrEmpty(privateUse) ? currentToken : privateUse + '-' + currentToken; } } variant = IetfLanguageTag.ConcatenateVariantAndPrivateUse(variant, privateUse); ConvertToPalasoConformPrivateUseRfc5646Tag(language, script, region, variant); }
public void SubTagComponentDescription_HasAliasFor_RemovesAliasFor() { Assert.AreEqual( "Japanese (Han + Hiragana + Katakana)", StandardSubtags.SubTagComponentDescription("Description: Japanese (alias for Han + Hiragana + Katakana)") ); }
[TestCase("A1B2", false)] // Valid script code that will is unlikely to be registered public void VerifyAddedPrivateUseScriptsMarkedProperly(string scriptCode, bool expectedValue) { StandardSubtags.RegisteredScripts.Remove(scriptCode); StandardSubtags.AddScript(scriptCode, "description"); Assert.AreEqual(StandardSubtags.RegisteredScripts[scriptCode].IsPrivateUse, expectedValue); StandardSubtags.RegisteredScripts.Remove(scriptCode); }
public void SubTagComponentDescription_HasBeginningParenthesis_RemovesParens() { Assert.AreEqual( "Hiragana + Katakana", StandardSubtags.SubTagComponentDescription("Description: (Hiragana + Katakana)") ); }
private void ValidateVariant() { var invalidPart = _variant.AllParts.FirstOrDefault(part => !StandardSubtags.IsValidRegisteredVariantCode(part)); if (!String.IsNullOrEmpty(invalidPart)) { throw new ValidationException( String.Format("'{0}' is not a valid registered variant code.", invalidPart) ); } _variant.ThrowIfSubtagContainsDuplicates(); }
///<summary>Constructor method to parse a valid RFC5646 tag as a string ///</summary> ///<param name="inputString">valid RFC5646 string</param> ///<returns>RFC5646Tag object</returns> public static Rfc5646Tag Parse(string inputString) { var tokens = inputString.Split(new[] { '-' }); var rfc5646Tag = new Rfc5646Tag(); bool haveX = false; for (int position = 0; position < tokens.Length; ++position) { var token = tokens[position]; if (token == "x") { haveX = true; continue; } if (haveX) { //This is the case for RfcTags consisting only of a private use subtag if (position == 1) { rfc5646Tag = new Rfc5646Tag(String.Empty, String.Empty, String.Empty, String.Empty, token); continue; } rfc5646Tag.AddToPrivateUse(token); continue; } if (position == 0) { rfc5646Tag.Language = token; continue; } if (position <= 1 && StandardSubtags.IsValidIso15924ScriptCode(token)) { rfc5646Tag.Script = token; continue; } if (position <= 2 && StandardSubtags.IsValidIso3166RegionCode(token)) { rfc5646Tag.Region = token; continue; } if (StandardSubtags.IsValidRegisteredVariantCode(token)) { rfc5646Tag.AddToVariant(token); continue; } throw new ValidationException(String.Format("The RFC tag '{0}' could not be parsed.", inputString)); } return(rfc5646Tag); }
public async Task <Attempt <ParatextUserInfo> > TryGetUserInfoAsync(User user) { if ((await TryCallApiAsync(_dataAccessClient, user, HttpMethod.Get, "projects")) .TryResult(out string response)) { var repos = XElement.Parse(response); var repoIds = new HashSet <string>(repos.Elements("repo").Select(r => (string)r.Element("projid"))); if ((await TryCallApiAsync(_registryClient, user, HttpMethod.Get, "projects")) .TryResult(out response)) { var projectArray = JArray.Parse(response); var projects = new List <ParatextProject>(); foreach (JToken projectObj in projectArray) { JToken identificationObj = projectObj["identification_systemId"] .FirstOrDefault(id => (string)id["type"] == "paratext"); if (identificationObj == null) { continue; } string projectId = (string)identificationObj["text"]; if (!repoIds.Contains(projectId)) { continue; } var langName = (string)projectObj["language_iso"]; if (StandardSubtags.TryGetLanguageFromIso3Code(langName, out LanguageSubtag subtag)) { langName = subtag.Name; } projects.Add(new ParatextProject { Id = projectId, Name = (string)identificationObj["fullname"], LanguageTag = (string)projectObj["language_ldml"], LanguageName = langName }); } return(Attempt.Success(new ParatextUserInfo { Username = GetUsername(user), Projects = projects })); } } return(Attempt <ParatextUserInfo> .Failure); }
private void ValidateRegion() { if (String.IsNullOrEmpty(_region)) { return; } if (_region.Contains("-")) { throw new ValidationException("The region tag may not contain dashes or underscores. I.e. there may only be a single iso 639 tag in this subtag"); } if (!StandardSubtags.IsValidIso3166RegionCode(_region)) { throw new ValidationException(String.Format("'{0}' is not a valid ISO-3166 region code.", _region)); } }
private void ValidateScript() { if (String.IsNullOrEmpty(_script)) { return; } if (_script.Contains("-")) { throw new ValidationException("The script tag may not contain dashes or underscores. I.e. there may only be a single iso 639 tag in this subtag"); } if (!StandardSubtags.IsValidIso15924ScriptCode(_script)) { throw new ValidationException(String.Format("'{0}' is not a valid ISO-15924 script code.", _script)); } }
private void ValidateLanguage() { if (String.IsNullOrEmpty(_language)) { return; } if (_language.Contains("-")) { throw new ValidationException( "The language tag may not contain dashes. I.e. there may only be a single iso 639 tag in this subtag" ); } if (!StandardSubtags.IsValidIso639LanguageCode(_language)) { throw new ValidationException(String.Format("'{0}' is not a valid ISO-639 language code.", _language)); } }
public static string ToValidVariantString(this string unknownString) { // var1-var2-var3 // var1-privateUse1-x-privateUse2 unknownString = unknownString.Trim(); unknownString = Regex.Replace(unknownString, @"[ ,.]", "-"); unknownString = Regex.Replace(unknownString, @"-+", "-"); var tokens = unknownString.Split('-'); var variants = new List <string>(); var privateUse = new List <string>(); bool haveSeenX = false; foreach (string token in tokens) { if (token == "x") { haveSeenX = true; continue; } if (!haveSeenX && StandardSubtags.IsValidRegisteredVariantCode(token)) { variants.Add(token); } else { privateUse.Add(token); } } string combinedVariant = String.Join("-", variants.ToArray()); string combinedPrivateUse = String.Join("-", privateUse.ToArray()); string variantString = combinedVariant; if (!String.IsNullOrEmpty(combinedPrivateUse)) { variantString = "x-" + combinedPrivateUse; if (!String.IsNullOrEmpty(combinedVariant)) { variantString = combinedVariant + "-" + variantString; } } return(variantString); }
/// <summary> /// A smarter way to get a name for an iso code. Recent rework on writing systems in libpalaso has /// apparently fixed much of our problems as StandardSubtags.TryGetLanguageFromIso3Code() finds 3-letter /// entries now. This adds fall-backs for 2-letter codes and strips off Script/Region/Variant codes. /// If we can't find ANY name, the out param is set to the isoCode itself, and we return false. /// </summary> /// <returns>true if it found a name</returns> public static bool GetBestLanguageName(this LanguageLookupModel isoModel, string isoCode, out string name) { // BL-8081/8096: Perhaps we got in here with Script/Region/Variant tag(s). // Try to get a match on the part of the isoCode up to the first hyphen. var codeToMatch = GetGeneralCode(isoCode.ToLowerInvariant()); if (!string.IsNullOrEmpty(codeToMatch)) { LanguageSubtag match; if (StandardSubtags.TryGetLanguageFromIso3Code(codeToMatch, out match)) { name = match.Name; return(true); } // Perhaps we only have a 2-letter code (e.g. 'fr'), in that case, this will likely find it. if (StandardSubtags.RegisteredLanguages.TryGet(codeToMatch, out match)) { name = match.Name; return(true); } } name = isoCode; // At this point, the best name we can come up with is the isoCode itself. return(false); }
/// <summary> /// Initializes a new instance of the <see cref="LanguageDataIndex"/> class. /// </summary> public LanguageDataIndex(IDictionary <string, string> sourcefiles) { string twotothreecodes = sourcefiles["TwoToThreeCodes.txt"]; string subtagregistry = sourcefiles["ianaSubtagRegistry.txt"]; StandardSubtags.InitialiseIanaSubtags(twotothreecodes, subtagregistry); // First read in Ethnologue data file into temporary dictionary var threeToTwoLetter = StandardSubtags.TwoAndThreeMap(twotothreecodes, true); //LanguageIndex.txt Format: LangID CountryID NameType Name //a language appears on one row for each of its alternative langauges string languageindex = sourcefiles["LanguageIndex.txt"]; var entries = new List <string>(languageindex.Split(new[] { "\n" }, StringSplitOptions.RemoveEmptyEntries)); entries.Add("qaa\t?\tL\tUnlisted Language"); foreach (string entry in entries.Skip(1)) //skip the header { string[] items = entry.Split('\t'); if (items.Length != 4) { continue; } if (items[2].StartsWith("!")) //temporary suppression of entries while waiting for Ethnologue changes { continue; } // excluded by ! // all gax (ET,KE,SO) including L // all gaz (ET) including L // all hae (ET) including L string code = items[0].Trim(); string twoLetterCode; string threelettercode = code; if (threeToTwoLetter.TryGetValue(code, out twoLetterCode)) { code = twoLetterCode; } //temporary suppression of entries while waiting for Ethnologue changes (those excluded by !) if (ExcludedCodes.Contains(code)) { continue; } string regionCode = items[1].Trim(); LanguageInfo language = GetOrCreateLanguageFromCode(code, threelettercode, regionCode == "?" ? "" : StandardSubtags.RegisteredRegions[regionCode].Name); string name = items[3].Trim(); if (items[2].Trim() == "L") { while (language.Names.Contains(name)) { language.Names.Remove(name); } language.Names.Insert(0, name); } else { if (items[2].Contains("P")) { //Skip pejorative } else if (ExcludedRegions.Contains(StandardSubtags.RegisteredRegions[regionCode].Name)) { //Skip alternatives for Ethiopia, as per request } else if (code == "gax" || code == "om") { //For these two "Oromo" languages, skip all related languages as per request } else if (!language.Names.Contains(name)) { language.Names.Add(name); //intentionally not lower-casing } } } // Then for each registered ietf language tag create a real entry and add the ethnologue data to it IOrderedEnumerable <LanguageSubtag> languages = StandardSubtags.RegisteredLanguages.OrderBy(lang => lang.Iso3Code); foreach (LanguageSubtag language in languages) { bool singlename = false; if (language.IsDeprecated || ExcludedCodes.Contains(language.Code)) { continue; } LanguageInfo langinfo = GetOrCreateLanguageFromCode(language.Code, language.Iso3Code, null); langinfo.DesiredName = language.Name.Replace("'", "’"); langinfo.IsMacroLanguage = language.IsMacroLanguage; foreach (string country in langinfo.Countries) { if (ExcludedRegions.Contains(country)) { singlename = true; } } foreach (string name in language.Names) { string langname = name.Replace("'", "’"); if (!langinfo.Names.Contains(langname)) { if (singlename && langinfo.Names.Count == 1) { // leave single ethnologue names break; } else { langinfo.Names.Add(langname); } } if (singlename) { break; } } _codeToLanguageIndex.Add(language.Code, langinfo); } IEnumerable <IGrouping <string, string> > languageGroups = Sldr.LanguageTags.Where(info => info.IsAvailable && IetfLanguageTag.IsValid(info.LanguageTag)) .Select(info => IetfLanguageTag.Canonicalize(info.LanguageTag)) .GroupBy(IetfLanguageTag.GetLanguagePart); foreach (IGrouping <string, string> languageGroup in languageGroups) { string[] langTags = languageGroup.ToArray(); if (langTags.Length == 1) { string langTag = langTags[0]; LanguageInfo language; if (langTag != languageGroup.Key && _codeToLanguageIndex.TryGetValue(languageGroup.Key, out language)) { _codeToLanguageIndex.Remove(languageGroup.Key); language.LanguageTag = langTag; _codeToLanguageIndex[langTag] = language; } } else { foreach (string langTag in langTags) { LanguageSubtag languageSubtag; ScriptSubtag scriptSubtag; RegionSubtag regionSubtag; IEnumerable <VariantSubtag> variantSubtags; if (IetfLanguageTag.TryGetSubtags(langTag, out languageSubtag, out scriptSubtag, out regionSubtag, out variantSubtags)) { if (langTag == languageSubtag) { continue; } LanguageInfo language = GetOrCreateLanguageFromCode(langTag, langTag, regionSubtag == null ? "" : regionSubtag.Name); // changed to default to "" 2017-04-24 bool displayScript = scriptSubtag != null && !IetfLanguageTag.IsScriptImplied(langTag); LanguageInfo otherLanguage; if (langTag != languageSubtag && !displayScript && _codeToLanguageIndex.TryGetValue(languageSubtag, out otherLanguage) && language.Countries.SetEquals(otherLanguage.Countries)) { language.Names.AddRange(otherLanguage.Names); } else { string name = displayScript ? string.Format("{0} ({1})", languageSubtag.Name, scriptSubtag.Name) : languageSubtag.Name; if (!language.Names.Contains(name)) { language.Names.Add(name); //intentionally not lower-casing } } LanguageInfo keylanguage; if (_codeToLanguageIndex.TryGetValue(languageGroup.Key, out keylanguage)) { language.IsMacroLanguage = keylanguage.IsMacroLanguage; } _codeToLanguageIndex.Add(langTag, language); } } } } string languagecodes = sourcefiles["LanguageCodes.txt"]; var codeentries = new List <string>(languagecodes.Split(new[] { "\n" }, StringSplitOptions.RemoveEmptyEntries)); foreach (var languageCode in codeentries) { var data = languageCode.Split(new[] { '\t' }, StringSplitOptions.RemoveEmptyEntries); if (data.Length < 2) { continue; } var langCode = data[0]; string twoLetterCode; if (threeToTwoLetter.TryGetValue(langCode, out twoLetterCode)) { langCode = twoLetterCode; } if (langCode == "fuv") { langCode = "fuv-Arab"; // special case because the script has been added to this language code } // which is probably something to do with the SLDR var countryCode = data[1]; LanguageInfo lang; if (_codeToLanguageIndex.TryGetValue(langCode, out lang)) { lang.PrimaryCountry = StandardSubtags.RegisteredRegions[countryCode].Name; } } // localise some language names foreach (LanguageInfo languageInfo in _codeToLanguageIndex.Values) { if (languageInfo.Names.Count == 0) { continue; // this language is suppressed } //Why just this small set? Only out of convenience. Ideally we'd have a db of all languages as they write it in their literature. string localName = null; switch (languageInfo.Names[0]) { case "French": localName = "français"; break; case "Spanish": localName = "español"; break; case "Chinese": localName = "中文"; break; case "Hindi": localName = "हिन्दी"; break; case "Bengali": localName = "বাংলা"; break; case "Telugu": localName = "తెలుగు"; break; case "Tamil": localName = "தமிழ்"; break; case "Urdu": localName = "اُردُو"; break; case "Arabic": localName = "العربية/عربي"; break; case "Thai": localName = "ภาษาไทย"; break; case "Indonesian": localName = "Bahasa Indonesia"; break; } if (!string.IsNullOrEmpty(localName)) { if (languageInfo.Names.Contains(localName)) { languageInfo.Names.Remove(localName); } languageInfo.Names.Insert(0, localName); languageInfo.DesiredName = localName; } switch (languageInfo.ThreeLetterTag) { case "itd": // 2 temporary special cases because the LanguageCodes.txt files needs to be updated with LanguageIndex.txt languageInfo.PrimaryCountry = "Indonesia"; break; case "xak": languageInfo.PrimaryCountry = "Venezuela"; break; default: // Also set the PrimaryCountry if there is only one country if (String.IsNullOrEmpty(languageInfo.PrimaryCountry) && languageInfo.Countries.Count == 1) { languageInfo.PrimaryCountry = languageInfo.Countries.First(); } break; } } // check if any languages are found in multiple countries but do not have a primary country // there is a test for this in LanguageLookupTests.llExpectedLanguagesHaveUniquePrimaryCountries var languagesWithoutRegions = new List <LanguageInfo>(); foreach (var lang in _codeToLanguageIndex.Values) { if (String.IsNullOrEmpty(lang.PrimaryCountry)) { languagesWithoutRegions.Add(lang); } } var languagesWithAmbiguousPrimaryCountry = languagesWithoutRegions.Where(l => l.Countries.Count() > 1); foreach (var lang in languagesWithAmbiguousPrimaryCountry) { Console.WriteLine("Language {0}({1}) has no primary country but is found in multiple countries", lang.DesiredName, lang.LanguageTag); } }
public void IsValidIso15924ScriptCode_Qaaa_ReturnsTrue() { Assert.That(StandardSubtags.IsValidIso15924ScriptCode("Qaaa"), Is.True); }
public void IsValidIso639LanguageCode_qaa_ReturnsTrue() { Assert.That(StandardSubtags.IsValidIso639LanguageCode("qaa"), Is.True); }
public void IsValidIso639LanguageCode_fonipa_ReturnFalse() { Assert.That(StandardSubtags.IsValidIso639LanguageCode("fonipa"), Is.False); }
public void IsValidRegisteredVariant_fonipa_ReturnsTrue() { Assert.That(StandardSubtags.IsValidRegisteredVariantCode("fonipa"), Is.True); }
public void IsValidRegisteredVariant_en_ReturnsFalse() { Assert.That(StandardSubtags.IsValidRegisteredVariantCode("en"), Is.False); }
public void IsValidIso3166Region_fonipa_ReturnsFalse() { Assert.That(StandardSubtags.IsValidIso3166RegionCode("fonipa"), Is.False); }
public void IsPrivateUseRegionCode_QM_ReturnsTrue() { Assert.That(StandardSubtags.IsPrivateUseRegionCode("QM"), Is.True); }
public void IsValidIso3166Region_QM_ReturnsTrue() { Assert.That(StandardSubtags.IsValidIso3166RegionCode("QM"), Is.True); Assert.That(StandardSubtags.IsValidIso3166RegionCode("qm"), Is.True); }
public void IsValidIso3166Region_US_ReturnsTrue() { Assert.That(StandardSubtags.IsValidIso3166RegionCode("US"), Is.True); }
public void IsValidIso639LanguageCode_two_ReturnTrue() { // Yes it's true Assert.That(StandardSubtags.IsValidIso639LanguageCode("two"), Is.True); }
public void IsPrivateUseScriptCode_Qaaa_ReturnsTrue() { Assert.That(StandardSubtags.IsPrivateUseScriptCode("Qaaa"), Is.True); }
public void IsValidIso15924ScriptCode_fonipa_ReturnsFalse() { Assert.That(StandardSubtags.IsValidIso15924ScriptCode("fonipa"), Is.False); }
/// <summary> /// Cleans the tag. /// </summary> public void Clean() { // Migrate legacy ISO3 language codes to IANA 2 letter language codes, if there's a match. // Do this before we look for valid codes, otherwise the 3-letter ones come up as invalid and // get moved to private use. However, only do this to languages not identified as private-use. if (!Language.StartsWith("x-", StringComparison.OrdinalIgnoreCase)) { string migrateFrom = ""; string migrateTo = ""; foreach (string part in _languageSubTag.AllParts) { if (part.Equals("x", StringComparison.OrdinalIgnoreCase)) { break; // don't migrate language code parts already explicitly marked private-use. } if (string.IsNullOrEmpty(migrateFrom)) { LanguageSubtag language; if (StandardSubtags.TryGetLanguageFromIso3Code(part, out language) && language.Code != language.Iso3Code) { migrateFrom = part; migrateTo = language.Code; } } } if (!String.IsNullOrEmpty(migrateFrom)) { _languageSubTag.RemoveParts(migrateFrom); _languageSubTag.AddToSubtag(migrateTo); } } // The very next thing, before anything else gets moved to private use, is to move the parts whose position we // care about to the appropriate position in the private use section. // In the process we may remove anything non-alphanumeric, since otherwise we may move a marker that later // disappears (pathologically). MoveFirstPartToPrivateUseIfNecessary(_languageSubTag, StandardSubtags.IsValidIso639LanguageCode, "qaa", true); MoveFirstPartToPrivateUseIfNecessary(_scriptSubTag, StandardSubtags.IsValidIso15924ScriptCode, "Qaaa", false); MoveFirstPartToPrivateUseIfNecessary(_regionSubTag, StandardSubtags.IsValidIso3166RegionCode, "QM", false); //This fixes a bug where the LdmlAdaptorV1 was writing out Zxxx as part of the variant to mark an audio writing system if (_variantSubTag.Contains(WellKnownSubtags.AudioScript)) { MoveTagsMatching(_variantSubTag, _scriptSubTag, tag => tag.Equals(WellKnownSubtags.AudioScript)); _privateUseSubTag.AddToSubtag(WellKnownSubtags.AudioPrivateUse); } // Fixes various legacy problems. if (Language.Equals("cmn", StringComparison.OrdinalIgnoreCase)) { Language = "zh"; } if (Language.Equals("pes", StringComparison.OrdinalIgnoreCase)) { Language = "fa"; } if (Language.Equals("arb", StringComparison.OrdinalIgnoreCase)) { Language = "ar"; } if (Language.Equals("zh", StringComparison.OrdinalIgnoreCase) && String.IsNullOrEmpty(Region)) { Region = "CN"; } // If the language tag contains an x- , then move the string behind the x- to private use MovePartsToPrivateUseIfNecessary(_languageSubTag); // Move script, region, and variant present in the langauge tag to their proper subtag. MoveTagsMatching(_languageSubTag, _scriptSubTag, StandardSubtags.IsValidIso15924ScriptCode, StandardSubtags.IsValidIso639LanguageCode); MoveTagsMatching(_languageSubTag, _regionSubTag, StandardSubtags.IsValidIso3166RegionCode, StandardSubtags.IsValidIso639LanguageCode); MoveTagsMatching(_languageSubTag, _variantSubTag, StandardSubtags.IsValidRegisteredVariantCode, StandardSubtags.IsValidIso639LanguageCode); // Move all other tags that don't belong to the private use subtag. //keep track of everything that we moved var tempSubTag = new SubTag(); MoveTagsMatching(_languageSubTag, tempSubTag, tag => !StandardSubtags.IsValidIso639LanguageCode(tag)); //place all the moved parts in private use. foreach (var part in tempSubTag.AllParts) { _privateUseSubTag.AddToSubtag(part); //if it looks like we moved a custom script set the subtag to mark that we've moved it if (_scriptSubTag.IsEmpty && part.Length == 4 && //potential custom script tag !WellKnownSubtags.IpaPhonemicPrivateUse.EndsWith(part) && !WellKnownSubtags.IpaPhoneticPrivateUse.EndsWith(part)) { _scriptSubTag = new SubTag("Qaaa"); } } MoveTagsMatching(_scriptSubTag, _privateUseSubTag, tag => !StandardSubtags.IsValidIso15924ScriptCode(tag)); MoveTagsMatching(_regionSubTag, _privateUseSubTag, tag => !StandardSubtags.IsValidIso3166RegionCode(tag)); MoveTagsMatching(_variantSubTag, _privateUseSubTag, tag => !StandardSubtags.IsValidRegisteredVariantCode(tag)); _languageSubTag.KeepFirstAndMoveRemainderTo(_privateUseSubTag); _scriptSubTag.KeepFirstAndMoveRemainderTo(_privateUseSubTag); _regionSubTag.KeepFirstAndMoveRemainderTo(_privateUseSubTag); if (_privateUseSubTag.Contains(WellKnownSubtags.AudioPrivateUse)) { // Move every tag that's not a Zxxx to private use if (!_scriptSubTag.IsEmpty && !_scriptSubTag.Contains(WellKnownSubtags.AudioScript)) { MoveTagsMatching(_scriptSubTag, _privateUseSubTag, tag => !_privateUseSubTag.Contains(tag)); } // If we don't have a Zxxx already, set it. This protects tags already present, but with unusual case if (!_scriptSubTag.Contains(WellKnownSubtags.AudioScript)) { _scriptSubTag = new SubTag(WellKnownSubtags.AudioScript); } } //These two methods may produce duplicates that will subsequently be removed. Do we care? - TA 29/3/2011 _privateUseSubTag.RemoveNonAlphaNumericCharacters(); _privateUseSubTag.TruncatePartsToNumCharacters(8); _variantSubTag.RemoveDuplicates(); _privateUseSubTag.RemoveDuplicates(); // Any 'x' in the other tags will have arrived in the privateUse tag, so remove them. _privateUseSubTag.RemoveParts("x"); // if language is empty, we need to add qaa, unless only a privateUse is present (e.g. x-blah is a valid rfc5646 tag) if ((_languageSubTag.IsEmpty && (!_scriptSubTag.IsEmpty || !_regionSubTag.IsEmpty || !_variantSubTag.IsEmpty)) || (_languageSubTag.IsEmpty && _scriptSubTag.IsEmpty && _regionSubTag.IsEmpty && _variantSubTag.IsEmpty && _privateUseSubTag.IsEmpty)) { _languageSubTag.AddToSubtag("qaa"); } // Two more legacy problems. We don't allow -etic or -emic without fonipa, so insert if needed. // If it has some other standard variant we won't be able to fix it...not sure what the right answer would be. // At least we catch the more common case. foreach (string part in _privateUseSubTag.AllParts) { if (string.IsNullOrEmpty(Variant) && (part.Equals("etic", StringComparison.OrdinalIgnoreCase) || part.Equals("emic", StringComparison.OrdinalIgnoreCase))) { Variant = "fonipa"; } } }
public async Task <IReadOnlyList <ParatextProject> > GetProjectsAsync(UserEntity user) { var accessToken = new JwtSecurityToken(user.ParatextTokens.AccessToken); Claim usernameClaim = accessToken.Claims.FirstOrDefault(c => c.Type == "username"); string username = usernameClaim?.Value; string response = await CallApiAsync(_dataAccessClient, user, HttpMethod.Get, "projects"); var reposElem = XElement.Parse(response); var repos = new Dictionary <string, string>(); foreach (XElement repoElem in reposElem.Elements("repo")) { var projId = (string)repoElem.Element("projid"); XElement userElem = repoElem.Element("users")?.Elements("user") ?.FirstOrDefault(ue => (string)ue.Element("name") == username); repos[projId] = (string)userElem?.Element("role"); } Dictionary <string, SFProjectEntity> existingProjects = (await _projects.Query() .Where(p => repos.Keys.Contains(p.ParatextId)) .ToListAsync()) .ToDictionary(p => p.ParatextId); response = await CallApiAsync(_registryClient, user, HttpMethod.Get, "projects"); var projectArray = JArray.Parse(response); var projects = new List <ParatextProject>(); foreach (JToken projectObj in projectArray) { JToken identificationObj = projectObj["identification_systemId"] .FirstOrDefault(id => (string)id["type"] == "paratext"); if (identificationObj == null) { continue; } string paratextId = (string)identificationObj["text"]; if (!repos.TryGetValue(paratextId, out string role)) { continue; } // determine if the project is connectable, i.e. either the project exists and the user hasn't been // added to the project, or the project doesn't exist and the user is the administrator bool isConnectable; bool isConnected = false; string projectId = null; if (existingProjects.TryGetValue(paratextId, out SFProjectEntity project)) { projectId = project.Id; isConnected = true; isConnectable = !project.Users.Any(u => u.UserRef == user.Id); } else if (role == SFProjectRoles.Administrator) { isConnectable = true; } else { isConnectable = false; } var langName = (string)projectObj["language_iso"]; if (StandardSubtags.TryGetLanguageFromIso3Code(langName, out LanguageSubtag subtag)) { langName = subtag.Name; } projects.Add(new ParatextProject { ParatextId = paratextId, Name = (string)identificationObj["fullname"], LanguageTag = (string)projectObj["language_ldml"], LanguageName = langName, ProjectId = projectId, IsConnectable = isConnectable, IsConnected = isConnected }); } return(projects); }