Request: Help check the language workers for accuracy

Started by ison, September 05, 2018, 07:45:23 AM

Previous topic - Next topic

ison

Here are all the LanguageWorkers currently integrated into RimWorld. If you notice any bug then please let us know!

A LanguageWorker is a piece of code which generates indefinite/definite/plural/etc. forms of words. They're handled like this because for example in some languages they depend on the gender of the noun.

Default
public abstract class LanguageWorker
{
public virtual string WithIndefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
if( str.NullOrEmpty() )
return "";

//By default names don't get articles
if( name )
return str;

if( Translator.CanTranslate( "IndefiniteForm" ) )
return "IndefiniteForm".Translate(str);
else
return "IndefiniteArticle".Translate() + " " + str;
}

public string WithIndefiniteArticle(string str, bool plural = false, bool name = false)
{
return WithIndefiniteArticle(str, LanguageDatabase.activeLanguage.ResolveGender(str), plural, name);
}

public string WithIndefiniteArticlePostProcessed(string str, Gender gender, bool plural = false, bool name = false)
{
return PostProcessed(WithIndefiniteArticle(str, gender, plural, name));
}

public string WithIndefiniteArticlePostProcessed(string str, bool plural = false, bool name = false)
{
return PostProcessed(WithIndefiniteArticle(str, plural, name));
}

public virtual string WithDefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
if( str.NullOrEmpty() )
return "";

//By default names don't get articles
if( name )
return str;

if( Translator.CanTranslate( "DefiniteForm" ) )
return "DefiniteForm".Translate(str);
else
return "DefiniteArticle".Translate() + " " + str;
}

public string WithDefiniteArticle(string str, bool plural = false, bool name = false)
{
return WithDefiniteArticle(str, LanguageDatabase.activeLanguage.ResolveGender(str), plural, name);
}

public string WithDefiniteArticlePostProcessed(string str, Gender gender, bool plural = false, bool name = false)
{
return PostProcessed(WithDefiniteArticle(str, gender, plural, name));
}

public string WithDefiniteArticlePostProcessed(string str, bool plural = false, bool name = false)
{
return PostProcessed(WithDefiniteArticle(str, plural, name));
}

public virtual string OrdinalNumber(int number, Gender gender = Gender.None)
{
return number.ToString();
}

public virtual string PostProcessed(string str)
{
//Fix double-spaces
str = str.MergeMultipleSpaces();

return str;
}

public virtual string ToTitleCase(string str)
{
if( str.NullOrEmpty() )
            return str;

return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(str);
}

public virtual string Pluralize(string str, Gender gender, int count = -1)
{
return str;
}

public string Pluralize(string str, int count = -1)
{
return Pluralize(str, LanguageDatabase.activeLanguage.ResolveGender(str), count);
}

public virtual string PostProcessedKeyedTranslation(string translation)
{
return translation;
}
}


Catalan
public class LanguageWorker_Catalan : LanguageWorker
{
public override string WithIndefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
if( name )
return WithElLaArticle(str, gender, true);
else if( plural )
return (gender == Gender.Female ? "unes " : "uns ") + str;
else
return (gender == Gender.Female ? "una " : "un ") + str;
}

public override string WithDefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
if( name )
return WithElLaArticle(str, gender, true);
else if( plural )
return (gender == Gender.Female ? "les " : "els ") + str;
else
return WithElLaArticle(str, gender, false);
}

private string WithElLaArticle(string str, Gender gender, bool name)
{
if( str.Length != 0 && (IsVowel(str[0]) || str[0] == 'h' || str[0] == 'H') )
{
if( name )
return (gender == Gender.Female ? "l'" : "n'") + str;
else
return "l'" + str;
}
else
return (gender == Gender.Female ? "la " : "el ") + str;
}

public override string OrdinalNumber(int number, Gender gender = Gender.None)
{
if( gender == Gender.Female )
return number + "a";
else if( number == 1 || number == 3 )
return number + "r";
else if( number == 2 )
return number + "n";
else if( number == 4 )
return number + "t";
else
return number + "è";
}

public bool IsVowel(char ch)
{
return "ieɛaoɔuəuàêèéòóüúIEƐAOƆUƏUÀÊÈÉÒÓÜÚ".IndexOf(ch) >= 0;
}
}


Danish
public class LanguageWorker_Danish : LanguageWorker
{
public override string WithIndefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
if( str.NullOrEmpty() )
return str;

//Names don't get articles
if( name )
return str;

if( gender == Gender.Male || gender == Gender.Female )
return "en " + str;
else
return "et " + str;
}

public override string WithDefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
if( str.NullOrEmpty() )
return str;

//Names don't get articles
if( name )
return str;

char last = str[str.Length - 1];

if( gender == Gender.Male || gender == Gender.Female )
{
if( last == 'e' )
return str + 'n';
else
return str + "en";
}
else
{
if( last == 'e' )
return str + 't';
else
return str + "et";
}
}
}


Dutch
public class LanguageWorker_Dutch : LanguageWorker
{
public override string WithIndefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
//Names don't get articles
if( name )
return str;

if( plural )
return str;
else
return "een " + str;
}

public override string WithDefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
//Names don't get articles
if( name )
return str;

if( plural )
return "de " + str;
else
{
if( gender == Gender.Male || gender == Gender.Female )
return "de " + str;
else
return "het " + str;
}
}
}


English
public class LanguageWorker_English : LanguageWorker
{
public override string WithIndefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
if( str.NullOrEmpty() )
return "";

//Names don't get articles
if( name )
return str;

if( plural )
return str;

return "a " + str;
}

public override string WithDefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
if( str.NullOrEmpty() )
return "";

//Names don't get articles
if( name )
return str;

return "the " + str;
}

public override string PostProcessed( string str )
{
str = base.PostProcessed(str);

//Correct a/an cases at the start of the string
if( str.StartsWith("a ", StringComparison.OrdinalIgnoreCase) && str.Length >= 3 )
{
bool hour = str.Substring(2) == "hour";

if( hour || str[2] == 'a' || str[2] == 'e' || str[2] == 'i' || str[2] == 'o' || str[2] == 'u' )
str = str.Insert(1, "n");
}

//Correct a/an cases in the middle of the string
str = str.Replace(" a a", " an a");
str = str.Replace(" a e", " an e");
str = str.Replace(" a i", " an i");
str = str.Replace(" a o", " an o");
str = str.Replace(" a u", " an u");
str = str.Replace(" a hour", " an hour");

str = str.Replace(" A a", " An a");
str = str.Replace(" A e", " An e");
str = str.Replace(" A i", " An i");
str = str.Replace(" A o", " An o");
str = str.Replace(" A u", " An u");
str = str.Replace(" A hour", " An hour");

return str;
}

public override string ToTitleCase(string str)
{
str = base.ToTitleCase(str);

str = str.Replace(" No. ", " no. ");
str = str.Replace(" The ", " the ");
str = str.Replace(" A ", " a ");
str = str.Replace(" For ", " for ");
str = str.Replace(" In ", " in ");
str = str.Replace(" With ", " with ");

return str;
}

public override string OrdinalNumber(int number, Gender gender = Gender.None)
{
int lastDigit = number % 10;
int secondLastDigit = (number / 10) % 10;

if( secondLastDigit != 1 )
{
if( lastDigit == 1 )
return number + "st";

if( lastDigit == 2 )
return number + "nd";

if( lastDigit == 3 )
return number + "rd";
}

return number + "th";
}

public override string Pluralize(string str, Gender gender, int count = -1)
{
if( str.NullOrEmpty() )
return str;

// it's ok if these rules are not perfect,
// we can usually specify the pluralized label in the Def if needed

char last = str[str.Length - 1];
char oneBeforeLast = str.Length == 1 ? '\0' : str[str.Length - 2];
bool oneBeforeLastIsVowel = char.IsLetter(oneBeforeLast) && "oaieuyOAIEUY".IndexOf(oneBeforeLast) >= 0;
bool oneBeforeLastIsConsonant = char.IsLetter(oneBeforeLast) && !oneBeforeLastIsVowel;

if( last == 'y' && oneBeforeLastIsConsonant )
return str.Substring(0, str.Length - 1) + "ies";
else
return str + "s";
}
}


French
public class LanguageWorker_French : LanguageWorker
{
public override string WithIndefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
//Names don't get articles
if( name )
return str;

if( plural )
return "des " + str;

return (gender == Gender.Female ? "une " : "un ") + str;
}

public override string WithDefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
if( str.NullOrEmpty() )
return str;

//Names don't get articles
if( name )
return str;

if( plural )
return "les " + str;

char first = str[0];

if( IsVowel(first) )
return "l'" + str;

return (gender == Gender.Female ? "la " : "le ") + str;
}

public override string OrdinalNumber(int number, Gender gender = Gender.None)
{
return number == 1 ? number + "er" : number + "e";
}

public override string Pluralize(string str, Gender gender, int count = -1)
{
if( str.NullOrEmpty() )
return str;

if( str[str.Length - 1] == 's' || str[str.Length - 1] == 'x' )
return str;
else
return str + "s";
}

public override string PostProcessed(string str)
{
return PostProcessedInt(base.PostProcessed(str));
}

public override string PostProcessedKeyedTranslation(string translation)
{
return PostProcessedInt(base.PostProcessedKeyedTranslation(translation));
}

public bool IsVowel(char ch)
{
return "iuyeøoɛœəɔaãɛ̃œ̃ɔ̃IUYEØOƐŒƏƆAÃƐ̃Œ̃Ɔ̃".IndexOf(ch) >= 0;
}

private string PostProcessedInt(string str)
{
return str.Replace(" de le ", " du ")
.Replace(" de les ", " des ")
.Replace(" de des ", " des ");
}
}


German
public class LanguageWorker_German : LanguageWorker
{
public override string WithIndefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
//Names don't get articles
if( name )
return str;

switch( gender )
{
case Gender.Male: return "ein " + str;
case Gender.Female: return "eine " + str;
case Gender.None: return "ein " + str;
default: return str;
}
}

public override string WithDefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
//Names don't get articles
if( name )
return str;

switch( gender )
{
case Gender.Male: return "der " + str;
case Gender.Female: return "die " + str;
case Gender.None: return "das " + str;
default: return str;
}
}

public override string OrdinalNumber(int number, Gender gender = Gender.None)
{
return number + ".";
}

public override string Pluralize(string str, Gender gender, int count = -1)
{
if( str.NullOrEmpty() )
return str;

char last = str[str.Length - 1];
char oneBeforeLast = str.Length >= 2 ? str[str.Length - 2] : '\0';

switch( gender )
{
case Gender.Male:
if( last == 'r' && oneBeforeLast == 'e' )
return str;
else if( last == 'l' && oneBeforeLast == 'e' )
return str;
else if( last == 'R' && oneBeforeLast == 'E' )
return str;
else if( last == 'L' && oneBeforeLast == 'E' )
return str;
else if( char.IsUpper(last) )
return str + 'E';
else
return str + 'e';

case Gender.Female:
if( last == 'e' )
return str + 'n';
else if( last == 'E' )
return str + 'N';
else if( last == 'n' && oneBeforeLast == 'i' )
return str + "nen";
else if( last == 'N' && oneBeforeLast == 'I' )
return str + "NEN";
else if( char.IsUpper(last) )
return str + "EN";
else
return str + "en";

case Gender.None:
if( last == 'r' && oneBeforeLast == 'e' )
return str;
else if( last == 'l' && oneBeforeLast == 'e' )
return str;
else if( last == 'n' && oneBeforeLast == 'e' )
return str;
else if( last == 'R' && oneBeforeLast == 'E' )
return str;
else if( last == 'L' && oneBeforeLast == 'E' )
return str;
else if( last == 'N' && oneBeforeLast == 'E' )
return str;
else if( char.IsUpper(last) )
return str + "EN";
else
return str + "en";

default:
return str;
}
}
}


Hungarian
public class LanguageWorker_Hungarian : LanguageWorker
{
public override string WithIndefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
//Names don't get articles
if( name )
return str;

return "egy " + str;
}

public override string WithDefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
if( str.NullOrEmpty() )
return str;

//Names don't get articles
if( name )
return str;

char first = str[0];

if( IsVowel(first) )
return "az " + str;
else
return "a " + str;
}

public bool IsVowel(char ch)
{
return "eéöőüűiíaáoóuúEÉÖŐÜŰIÍAÁOÓUÚ".IndexOf(ch) >= 0;
}
}


Italian
public class LanguageWorker_Italian : LanguageWorker
{
public override string WithIndefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
//Names don't get articles
if( name )
return str;

char first = str[0];
char second = str.Length >= 2 ? str[1] : '\0';

if( gender == Gender.Female )
{
if( IsVowel(first) )
return "un'" + str;
else
return "una " + str;
}
else
{
char firstLow = char.ToLower(first);
char secondLow = char.ToLower(second);

if( (first == 's' || first == 'S') && !IsVowel(second) )
return "uno " + str;
else if( (firstLow == 'p' && secondLow == 's') || (firstLow == 'p' && secondLow == 'n') || firstLow == 'z' || firstLow == 'x' || firstLow == 'y' || (firstLow == 'g' && secondLow == 'n') )
return "uno " + str;
else
return "un " + str;
}
}

public override string WithDefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
if( str.NullOrEmpty() )
return str;

//Names don't get articles
if( name )
return str;

char first = str[0];
char second = str.Length >= 2 ? str[1] : '\0';

if( gender == Gender.Female )
{
if( IsVowel(first) )
return "l'" + str;
else
return "la " + str;
}
else
{
if( first == 'z' || first == 'Z' )
return "lo " + str;
else if( (first == 's' || first == 'S') && !IsVowel(second) )
return "lo " + str;
else if( IsVowel(first) )
return "l'" + str;
else
return "il " + str;
}
}

public bool IsVowel(char ch)
{
return "aeiouAEIOU".IndexOf(ch) >= 0;
}

public override string OrdinalNumber(int number, Gender gender = Gender.None)
{
return number + "°";
}

public override string Pluralize(string str, Gender gender, int count = -1)
{
if( str.NullOrEmpty() )
return str;

char last = str[str.Length - 1];

if( !IsVowel(last) )
return str;
else if( gender == Gender.Female )
return str.Substring(0, str.Length - 1) + 'e';
else
return str.Substring(0, str.Length - 1) + 'i';
}
}


Korean
public class LanguageWorker_Korean : LanguageWorker
{
public override string PostProcessed(string str)
{
str = base.PostProcessed(str);
str = ReplaceJosa(str);

return str;
}

public override string PostProcessedKeyedTranslation(string translation)
{
translation = base.PostProcessedKeyedTranslation(translation);
translation = ReplaceJosa(translation);

return translation;
}

//--------------------- ReplaceJosa -----------------------

//Types
private struct JosaPair
{
public readonly string josa1, josa2;

public JosaPair(string josa1, string josa2)
{
this.josa1 = josa1;
this.josa2 = josa2;
}
}

//Working vars
private static StringBuilder tmpStringBuilder = new StringBuilder();

//Constants
private static readonly Regex JosaPattern = new Regex(@"\(이\)가|\(와\)과|\(을\)를|\(은\)는|\(아\)야|\(이\)여|\(으\)로|\(이\)라");
private static readonly Dictionary<string, JosaPair> JosaPatternPaired = new Dictionary<string, JosaPair>
{
{"(이)가", new JosaPair("이", "가")},
{"(와)과", new JosaPair("과", "와")},
{"(을)를", new JosaPair("을", "를")},
{"(은)는", new JosaPair("은", "는")},
{"(아)야", new JosaPair("아", "야")},
{"(이)여", new JosaPair("이여", "여")},
{"(으)로", new JosaPair("으로", "로")},
{"(이)라", new JosaPair("이라", "라")}
};

private static readonly List<char> AlphabetEndPattern = new List<char> { 'b', 'c', 'k', 'l', 'm', 'n', 'p', 'q', 't' };

public string ReplaceJosa(string src)
{
tmpStringBuilder.Length = 0;
var josaMatches = JosaPattern.Matches(src);
var lastHeadIndex = 0;

for( int i = 0; i < josaMatches.Count; i++ )
{
var josaMatch = josaMatches[i];
var matchingPair = JosaPatternPaired[josaMatch.Value];

tmpStringBuilder.Append(src, lastHeadIndex, josaMatch.Index - lastHeadIndex);

if( josaMatch.Index > 0 )
{
var prevChar = src[josaMatch.Index - 1];
if ((josaMatch.Value != "(으)로" && HasJong(prevChar)) ||
(josaMatch.Value == "(으)로" && HasJongExceptRieul(prevChar)))
{
tmpStringBuilder.Append(matchingPair.josa1);
}
else
tmpStringBuilder.Append(matchingPair.josa2);
}
else
tmpStringBuilder.Append(matchingPair.josa1);

lastHeadIndex = josaMatch.Index + josaMatch.Length;
}

tmpStringBuilder.Append(src, lastHeadIndex, src.Length - lastHeadIndex);

return tmpStringBuilder.ToString();
}

private bool HasJong(char inChar)
{
if( !IsKorean(inChar) )
return AlphabetEndPattern.Contains(inChar);

var localCode = inChar - 0xAC00; // 가~ 이후 로컬 코드
var jongCode = localCode % 28;
return jongCode > 0;
}

private bool HasJongExceptRieul(char inChar)
{
if( !IsKorean(inChar) )
return false;

var localCode = inChar - 0xAC00;
var jongCode = localCode % 28;
return jongCode != 8 && jongCode != 0;
}

private bool IsKorean(char inChar)
{
return inChar >= 0xAC00 && inChar <= 0xD7A3;
}
}

ison

#1
Norwegian
public class LanguageWorker_Norwegian : LanguageWorker
{
public override string WithIndefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
//Names don't get articles
if( name )
return str;

if( gender == Gender.Male || gender == Gender.Female )
return "en " + str;
else
return "et " + str;
}

public override string WithDefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
if( str.NullOrEmpty() )
return str;

//Names don't get articles
if( name )
return str;

char last = str[str.Length - 1];

if( gender == Gender.Male || gender == Gender.Female )
{
if( last == 'e' )
return str + 'n';
else
return str + "en";
}
else
{
if( last == 'e' )
return str + 't';
else
return str + "et";
}
}
}


Portuguese
public class LanguageWorker_Portuguese : LanguageWorker
{
public override string WithIndefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
//Names don't get articles
if( name )
return str;

if( plural )
return (gender == Gender.Female ? "umas " : "uns ") + str;
else
return (gender == Gender.Female ? "uma " : "um ") + str;
}

public override string WithDefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
//Names don't get articles
if( name )
return str;

if( plural )
return (gender == Gender.Female ? "as " : "os ") + str;
else
return (gender == Gender.Female ? "a " : "o ") + str;
}
}


Romanian
public class LanguageWorker_Romanian : LanguageWorker
{
public override string WithIndefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
//Names don't get articles
if( name )
return str;

if( plural )
return gender == Gender.Male ? str + 'i' : str + 'e';
else
return (gender == Gender.Female ? "a " : "un ") + str;
}

public override string WithDefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
if( str.NullOrEmpty() )
return str;

//Names don't get articles
if( name )
return str;

char last = str[str.Length - 1];

if( plural )
return gender == Gender.Male ? str + 'i' : str + 'e';
else
{
if( !IsVowel(last) )
return str + "ul";
else if( gender == Gender.Male )
return str + "le";
else
return str + 'a';
}
}

public bool IsVowel(char ch)
{
return "aeiouâîAEIOUÂÎ".IndexOf(ch) >= 0;
}
}


Russian
public class LanguageWorker_Russian : LanguageWorker
{
private interface IResolver
{
string Resolve(string[] arguments);
}

private class ReplaceResolver : IResolver
{
// ^Replace('{0}', 'Мартомай'-'Мартомая', 'Июгуст'-'Июгуста', 'Сентоноябрь'-'Сентоноября', 'Декавраль'-'Декавраля')^
private static readonly Regex _argumentRegex = new Regex(@"'(?<old>[^']*?)'-'(?<new>[^']*?)'", RegexOptions.Compiled);

public string Resolve(string[] arguments)
{
if(arguments.Length == 0)
{
return null;
}

string input = arguments[0];

if (arguments.Length == 1)
{
return input;
}

for (int i = 1; i < arguments.Length; ++i)
{
string argument = arguments[i];

Match match = _argumentRegex.Match(argument);
if (!match.Success)
{
return null;
}

string oldValue = match.Groups["old"].Value;
string newValue = match.Groups["new"].Value;

if(oldValue == input)
{
return newValue;
}
//Log.Message(string.Format("input: {0}, old: {1}, new: {2}", input, oldGroup.Captures[i].Value, newGroup.Captures[i].Value));
}

return input;
}
}

private class NumberCaseResolver : IResolver
{
// '3.14': 1-'прошёл # день', 2-'прошло # дня', X-'прошло # дней'
private static readonly Regex _numberRegex = new Regex(@"(?<floor>[0-9]+)(\.(?<frac>[0-9]+))?", RegexOptions.Compiled);

public string Resolve(string[] arguments)
{
if (arguments.Length != 4)
{
return null;
}

string numberStr = arguments[0];
Match numberMatch = _numberRegex.Match(numberStr);
if (!numberMatch.Success)
{
return null;
}

bool hasFracPart = numberMatch.Groups["frac"].Success;

string floorStr = numberMatch.Groups["floor"].Value;

string formOne = arguments[1].Trim('\'');
string formSeveral = arguments[2].Trim('\'');
string formMany = arguments[3].Trim('\'');

if (hasFracPart)
{
return formSeveral.Replace("#", numberStr);
}

int floor = int.Parse(floorStr);
return GetFormForNumber(floor, formOne, formSeveral, formMany).Replace("#", numberStr);
}

private static string GetFormForNumber(int number, string formOne, string formSeveral, string formMany)
{
int firstPos = number % 10;
int secondPos = number / 10 % 10;

if (secondPos == 1)
{
return formMany;
}

switch (firstPos)
{
case 1:
return formOne;
case 2:
case 3:
case 4:
return formSeveral;
default:
return formMany;
}
}
}

private static readonly ReplaceResolver replaceResolver = new ReplaceResolver();
private static readonly NumberCaseResolver numberCaseResolver = new NumberCaseResolver();

private static readonly Regex _languageWorkerResolverRegex = new Regex(@"\^(?<resolverName>\w+)\(\s*(?<argument>[^|]+?)\s*(\|\s*(?<argument>[^|]+?)\s*)*\)\^", RegexOptions.Compiled);

public override string PostProcessedKeyedTranslation(string translation)
{
translation = base.PostProcessedKeyedTranslation(translation);
return PostProcess(translation);
}

public override string PostProcessed(string str)
{
str = base.PostProcessed(str);
return PostProcess(str);
}

private static string PostProcess(string translation)
{
return _languageWorkerResolverRegex.Replace(translation, EvaluateResolver);
}

private static string EvaluateResolver(Match match)
{
string keyword = match.Groups["resolverName"].Value;

Group argumentsGroup = match.Groups["argument"];

string[] arguments = new string[argumentsGroup.Captures.Count];
for(int i = 0; i < argumentsGroup.Captures.Count; ++i)
{
arguments[i] = argumentsGroup.Captures[i].Value.Trim();
}

IResolver resolver = GetResolverByKeyword(keyword);

if( resolver == null )
return match.Value;

string result = resolver.Resolve(arguments);
if(result == null)
{
Log.ErrorOnce(string.Format("Error happened while resolving LW instruction: \"{0}\"", match.Value), match.Value.GetHashCode() ^ 374656432);
return match.Value;
}

return result;
}

private static IResolver GetResolverByKeyword(string keyword)
{
switch (keyword)
{
case "Replace":
return replaceResolver;
case "Number":
return numberCaseResolver;
default:
return null;
}
}

public override string Pluralize(string str, Gender gender, int count = -1)
{
if (str.NullOrEmpty())
{
return str;
}
char c = str[str.Length - 1];
char c2 = (str.Length < 2) ? '\0' : str[str.Length - 2];
if (gender != Gender.Male)
{
if (gender != Gender.Female)
{
if (gender == Gender.None)
{
if (c == 'o')
{
return str.Substring(0, str.Length - 1) + 'a';
}
if (c == 'O')
{
return str.Substring(0, str.Length - 1) + 'A';
}
if (c == 'e' || c == 'E')
{
char value = char.ToUpper(c2);
if ("ГКХЖЧШЩЦ".IndexOf(value) >= 0)
{
if (c == 'e')
{
return str.Substring(0, str.Length - 1) + 'a';
}
if (c == 'E')
{
return str.Substring(0, str.Length - 1) + 'A';
}
}
else
{
if (c == 'e')
{
return str.Substring(0, str.Length - 1) + 'я';
}
if (c == 'E')
{
return str.Substring(0, str.Length - 1) + 'Я';
}
}
}
}
}
else
{
if (c == 'я')
{
return str.Substring(0, str.Length - 1) + 'и';
}
if (c == 'ь')
{
return str.Substring(0, str.Length - 1) + 'и';
}
if (c == 'Я')
{
return str.Substring(0, str.Length - 1) + 'И';
}
if (c == 'Ь')
{
return str.Substring(0, str.Length - 1) + 'И';
}
if (c == 'a' || c == 'A')
{
char value2 = char.ToUpper(c2);
if ("ГКХЖЧШЩ".IndexOf(value2) >= 0)
{
if (c == 'a')
{
return str.Substring(0, str.Length - 1) + 'и';
}
return str.Substring(0, str.Length - 1) + 'И';
}
else
{
if (c == 'a')
{
return str.Substring(0, str.Length - 1) + 'ы';
}
return str.Substring(0, str.Length - 1) + 'Ы';
}
}
}
}
else
{
if (IsConsonant(c))
{
return str + 'ы';
}
if (c == 'й')
{
return str.Substring(0, str.Length - 1) + 'и';
}
if (c == 'ь')
{
return str.Substring(0, str.Length - 1) + 'и';
}
if (c == 'Й')
{
return str.Substring(0, str.Length - 1) + 'И';
}
if (c == 'Ь')
{
return str.Substring(0, str.Length - 1) + 'И';
}
}
return str;
}

private static bool IsConsonant(char ch)
{
return "бвгджзклмнпрстфхцчшщБВГДЖЗКЛМНПРСТФХЦЧШЩ".IndexOf(ch) >= 0;
}
}


Spanish
public class LanguageWorker_Spanish : LanguageWorker
{
public override string WithIndefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
//Names don't get articles
if( name )
return str;

return (gender == Gender.Female ? "una " : "un ") + str;
}

public override string WithDefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
//Names don't get articles
if( name )
return str;

return (gender == Gender.Female ? "la " : "el ") + str;
}

public override string OrdinalNumber(int number, Gender gender = Gender.None)
{
return number + ".º";
}

public override string Pluralize(string str, Gender gender, int count = -1)
{
if( str.NullOrEmpty() )
return str;

char last = str[str.Length - 1];
char oneBeforeLast = str.Length >= 2 ? str[str.Length - 2] : '\0';

if( IsVowel(last) )
{
if( str == "sí" )
return "síes";
else if( last == 'í' || last == 'ú' || last == 'Í' || last == 'Ú' )
return str + "es";
else
return str + 's';
}
else
{
if( (last == 'y' || last == 'Y') && IsVowel(oneBeforeLast) )
return str + "es";
else if( "lrndzjsxLRNDZJSX".IndexOf(last) >= 0 || (last == 'h' && oneBeforeLast == 'c') )
return str + "es";
else
return str + 's';
}
}

public bool IsVowel(char ch)
{
return "aeiouáéíóúAEIOUÁÉÍÓÚ".IndexOf(ch) >= 0;
}
}


Swedish
public class LanguageWorker_Swedish : LanguageWorker
{
public override string WithIndefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
//Names don't get articles
if( name )
return str;

if( gender == Gender.Male || gender == Gender.Female )
return "en " + str;
else
return "ett " + str;
}

public override string WithDefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
if( str.NullOrEmpty() )
return str;

//Names don't get articles
if( name )
return str;

char last = str[str.Length - 1];

if( gender == Gender.Male || gender == Gender.Female )
{
if( IsVowel(last) )
return str + 'n';
else
return str + "en";
}
else
{
if( IsVowel(last) )
return str + 't';
else
return str + "et";
}
}

public bool IsVowel(char ch)
{
return "aeiouyåäöAEIOUYÅÄÖ".IndexOf(ch) >= 0;
}
}


Turkish
public class LanguageWorker_Turkish : LanguageWorker
{
public override string WithIndefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
//Names don't get articles
if( name )
return str;

return str + " bir";
}

public override string WithDefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
return str;
}
}

b606

Thanks ison !
For LanguageWorker_French,


public class LanguageWorker_French : LanguageWorker
{
public override string WithIndefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
//Names don't get articles
if( name )
return str;
               if( plural )
return "des " + str;
return (gender == Gender.Female ? "une " : "un ") + str;
}

public override string WithDefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
//Names don't get articles
if( name )
return str;
                if( plural )
                        return "les " + str;

                char first = str[0];
if( IsVowel(first) )
return "l'" + str;
return (gender == Gender.Female ? "la " : "le ") + str;
}

public override string OrdinalNumber(int number)
{
return number == 1 ? number + "er" : number + "ème";
                // for gender female, should be 1ère instead of 1er
}

public override string Pluralize(string str, Gender gender, int count = -1)
{
if( str.NullOrEmpty() )
return str;

if( str[str.Length - 1] == 's' || str[str.Length - 1] == 'x' )
return str;
else
return str + "s";
}
}


PostProcessing:
I think that there should be also additional "PostProcessed" code to replace " de le " by " du ", and " de les " by " des ", for the inclusion of the definite article in sentences with " de ", such as "the head of the muffalo" -> "la tête de le muffalo" (incorrect)-> "la tête du muffalo" (correct).

For gender female " de la " and definite with vowel " de l'{vowel} " are correct.

For indefinite article and more generally any str starting with a vowel, " de {vowel}" becomes "d'{vowel}". Ex. "the head of a muffalo" -> "la tête de un muffalo" -> "la tête d'un muffalo"

Last, " de des " -> "des"
b606


Make the French RimWorld Translation at maximum quality.
French Optimisation Mod

mecatxis

#3
Thanks Ison!

I am reworking for Catalan. I am not a programmer, so please, excuse my mistakes and misunderstandings.
The article should be contracted when started with vowel or h (el Carles, l'Òscar, la Rosa, l'Anna). There is some exceptions if the first syllable is tonic an others, but i understand it will be very difficult to tract. so:

public override string WithIndefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
if( name )
char first = str[0];
if (IsVowelorh(first)
{
return (gender == Gender.Female ? "l/'" : "n/'") + str;
}
else
{
return (gender == Gender.Female ? "la " : "el ") + str;
}
else if( plural )
return (gender == Gender.Female ? "unes " : "uns ") + str;
else
return (gender == Gender.Female ? "una " : "un ") + str;
}
public override string WithDefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
if( name )
char first = str[0];
if (IsVowelorh(first)
{
return (gender == Gender.Female ? "l/'" : "n/'") + str;
}
else
{
return (gender == Gender.Female ? "la " : "el ") + str;
}
else if( plural )
return (gender == Gender.Female ? "les " : "els ") + str;
else
return (gender == Gender.Female ? "la " : "el ") + str;
}
public bool IsVowelorh(char ch)
{
return "aeiouàèéíòóúïüAEIOUÀÈÉÍÒÓÚÏÜ".IndexOf(ch) >= 0;
}


Ordinals can change depending on number and the gender of the thing counted. I have seen them treated on some workers. I don't know if is possible something like this:

public override string OrdinalNumber(int number, Gender gender)
{
if ( gender == Gender.Female)
{
return number + "a";
}
else
{
if( int == 1 or int == 3)
return number + "r";
else if (int == 2)
return number + "n";
else if (int == 4)
return number + "t";
else
return number + "è";
}
}



Thanks again! Very good job!

ison

Great, thank you for your feedback.

I've modified both French and Catalan language workers. You can see the updated code in my first post.

And please don't worry if the code is difficult to read. It's perfectly fine to just describe the problem in English or use pseudo-code.

In Catalan, I'm not sure if I got the 'l 'n right. Currently it's like this:
If it's indefinite, it's a name, and the first character is either a vowel or h: use either 'l or 'n depending on the gender.
If it's definite, it's a name, and the first character is either a vowel or h: use either 'l or 'n depending on the gender.
If it's definite, it's not a name, and the first character is either a vowel or h: always use 'l

mecatxis

QuoteIf it's indefinite, it's a name, and the first character is either a vowel or h: use either 'l or 'n depending on the gender.
If it's definite, it's a name, and the first character is either a vowel or h: use either 'l or 'n depending on the gender.
If it's definite, it's not a name, and the first character is either a vowel or h: always use 'l

it is not 'l. It is l', as in l'Oriol (man) or l'Hortènsia (woman)

I can work with this simplification of the rule , more or less.

Here is the complete rule https://www.cursdecatala.com/en/catalan-apostrophe/



mecatxis

#6
Pluralize in Catalan.
You can find the rule here: http://mylanguages.org/catalan_plural.php
Usualy only needs to add -s

Finishing with -a changes to -es, except for ça goes to ces, -ca goes to -ques, -qua goes to -qües, -ja goes to -ges, -ga goes to -gues.

Finishing with -s. Basicaly:
- finishing with -às goes to -assos
- finishing with -és goes to -essos
- finishing with -es goes to -esos
- finishing with -és goes to -issos
- finishing with -ós, òs or monosyllables (hard to know automatically) ending with -os goes to -ossos
- finishing with -ús goes to -ussos

The hard thing will be
Quotewords ending in a stressed vowel form the plural by adding (-ns)
. Does any other language solved this problem? Any idea how to detect a stressed syllabe?

- UPDATED
I misunderstood the problem.It is as easy as words ending in "àéèíòóú" pluralize adding -ns. Thanks!


b606

For LanguageWorker_French, after further testing and checks

1) The letters "yY" should be removed from the vowels list since definite article for these is not "l'". Ex : "the yacht" ->  "le yacht" not "l'yacht".

2) Most of words starting with "h" (case of so called 'h muet') get " l' " but there are exceptions. Ex: "the man" -> "l'homme", "l'hortensia" but "la hotte". Unless someone could define the grammatical rule for that, I would treat this as a general case and process the exception differently. The idea is that in RimWorld, the words with that exception are really rare.

3) Additional PostProcessed :
a) "if [*_pronoun]" -> "if he" -> "si il" -> "s'il" ("if she" -> "si elle" is correct)
b) " de {vowel}..." -> " d'{vowel} " : as a pseudocode, would be
    - find " de " in str
    - check if next char c IsVowel(c), if true replace only that " de " with " d'"


public bool IsVowel(char ch)
{
return "hiueøoɛœəɔaãɛ̃œ̃ɔ̃IHUEØOƐŒƏƆAÃƐ̃Œ̃Ɔ̃".IndexOf(ch) >= 0;
}


private string PostProcessedInt(string str)
{
return str.Replace(" de le ", " du ")
.Replace(" de les ", " des ")
.Replace(" de des ", " des ")
.Replace(" si il ", " s'il ")
.Replace(" Si il ", " S'il ");
}


Best regards !
b606


Make the French RimWorld Translation at maximum quality.
French Optimisation Mod

mecatxis

I am trying to implement it, but not guessing how is supposed to work. Checking for "Do you really want to banish {1_label}?":

1 - Esteu segur que voleu fer fora WithDefiniteArticle({1_label})?
Got "Esteu segur que voleu fer fora WithDefiniteArticle(Pree, Herborista)?"

2 - Esteu segur que voleu fer fora (WithDefiniteArticle({1_label}))?
Got "Esteu segur que voleu fer fora (WithDefiniteArticle(Pree, Herborista))?"

3 - Esteu segur que voleu fer fora {WithDefiniteArticle(1_label)}?
Gets "Esteu segur que voleu fer fora ?

Other combinations give similar wrong results. Can anybody help?

b606

I do not speak this language at all (though same family language.). So, what is wrong with
- "Esteu segur que voleu fer fora {1_label} ?"
or
- "Esteu segur que voleu fer fora {1_nameDef} ?"
b606


Make the French RimWorld Translation at maximum quality.
French Optimisation Mod

mecatxis

QuoteI do not speak this language at all (though same family language.). So, what is wrong with
- "Esteu segur que voleu fer fora {1_label} ?"
or
- "Esteu segur que voleu fer fora {1_nameDef} ?"

I will try to explain better. I am trying to use "WithDefiniteArticle" in the translation of the sentence "Do you really want to banish {1_label}" (this is how it shows in the original file).

I expected to get "Esteu segur que voleu fer fora el John" or "Esteu segur que voleu fer fora la Mary" depending on the gender of the subject.

I am asking how do i should implement the "WithDefiniteArticle" worker.


Varisha

Hi,

this is a great feature to get things more flexible. I wonder how to trigger the Pluralize method using the games mechanics to check if the change is ok.
Can you give me short help on this? :)

mecatxis

Quote from: mecatxis on September 13, 2018, 03:12:40 AM
QuoteI do not speak this language at all (though same family language.). So, what is wrong with
- "Esteu segur que voleu fer fora {1_label} ?"
or
- "Esteu segur que voleu fer fora {1_nameDef} ?"

I will try to explain better. I am trying to use "WithDefiniteArticle" in the translation of the sentence "Do you really want to banish {1_label}" (this is how it shows in the original file).

I expected to get "Esteu segur que voleu fer fora el John" or "Esteu segur que voleu fer fora la Mary" depending on the gender of the subject.

I am asking how do i should implement the "WithDefiniteArticle" worker.

I finally found the answer: "Esteu segur que voleu fer fora {1_definite}?"

mecatxis

For {0_objective} could i have
"la" if is female. Otherwise "lo".
?

Thanks

Angiel

#14
Quote from: ison on September 05, 2018, 07:45:39 AM

Romanian

if( plural )
return gender == Gender.Male ? str + 'i' : str + 'e';
else
return (gender == Gender.Female ? "a " : "un ") + str;



So in this part of the code, i think you are trying to determine the gender of the word by the indefinite article of the word.
In romanian the feminine words have the indefinite article "o " in front not "a " like stated in the code.
e. g. o femeie, o mașină, o sfoară etc.

And we don't have only 2 genders sadly :)). we have a third gender which is neutral
The indefinite article in singular form is "un " like masculine words, but the plural is formed like the feminine one.

e.g. un scaun, plural: scaune = scaun + e (feminin plural); (chair)
      un geamantan, plural: geamantane (suitcase, trunk)

and if i understand again the code right, which i am not very sure since the terminology isn't familiar to me :D. you only form or search the plural for feminine with termination "e" and for masculine "i".
in that case there are plural words that end in "i" that are actually feminine,
e.g. o vaca, plural: vaci  ( cow, which is actually in the game)
      o minte, plural: minți (this means mind)

i can't say if there is a rule for this or if there are just some words which make an exception, ironically
e.g. o excepție, plural: excepții which means exception if you haven't guessed already :D.
I think it has to do with the way the word ends because "vace" would sound wrong, and the others seem to end in "e" already so it can't have double "ee" to form the plural.

I could help further but like i said i don't really understand the logic of the code, as in what the conditions are searching for and what should the functions return.

For example this part of the code
what i understand is that here you are trying to give the words the definitive article form of the words.


if( !IsVowel(last) )
return str + "ul";
else if( gender == Gender.Male )
return str + "le";
else
return str + 'a';



so my logic of the code would be
1 it puts a condition if the last letter of the word doesn't end in a vowel
2 it should add the termination "ul"
3 if it does end in a vowel it should see if the gender of the word is masculine
4 if masculine should add the termination "le" ;;; termination "le" is the definitive article form for plural words not for masculine, the "ul" termination is for singular form masculine and neutral genders.
5 if not masculine, hence feminine add the termination "a" ;;; which is correct.

well i won't try and say anything further i think i said a lot and if i say more and i am understanding the code wrong then i am doing stuff in vain :).