Ludeon Forums

RimWorld => Mods => Translations => Topic started by: ison on September 05, 2018, 07:45:23 AM

Title: Request: Help check the language workers for accuracy
Post by: ison on September 05, 2018, 07:45:23 AM
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;
}
}
Title: Re: Translators! Please check the language workers
Post by: ison on September 05, 2018, 07:45:39 AM
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;
}
}
Title: Re: Request: Help check the language workers for accuracy
Post by: b606 on September 06, 2018, 11:23:45 AM
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"
Title: Re: Request: Help check the language workers for accuracy
Post by: mecatxis on September 06, 2018, 03:33:21 PM
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!
Title: Re: Request: Help check the language workers for accuracy
Post by: ison on September 07, 2018, 10:27:31 AM
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
Title: Re: Request: Help check the language workers for accuracy
Post by: mecatxis on September 07, 2018, 02:20:42 PM
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/ (https://www.cursdecatala.com/en/catalan-apostrophe/)


Title: Re: Request: Help check the language workers for accuracy
Post by: mecatxis on September 08, 2018, 06:27:47 AM
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!

Title: Re: Request: Help check the language workers for accuracy
Post by: b606 on September 08, 2018, 07:34:45 AM
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 !
Title: Re: Request: Help check the language workers for accuracy
Post by: mecatxis on September 11, 2018, 05:07:53 AM
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?
Title: Re: Request: Help check the language workers for accuracy
Post by: b606 on September 12, 2018, 06:05:41 AM
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} ?"
Title: Re: Request: Help check the language workers for accuracy
Post by: 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.

Title: Re: Request: Help check the language workers for accuracy
Post by: Varisha on September 13, 2018, 03:31:08 AM
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? :)
Title: Re: Request: Help check the language workers for accuracy
Post by: mecatxis on September 13, 2018, 02:36:09 PM
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}?"
Title: Re: Request: Help check the language workers for accuracy
Post by: mecatxis on September 17, 2018, 02:27:06 PM
For {0_objective} could i have
"la" if is female. Otherwise "lo".
?

Thanks
Title: Re: Translators! Please check the language workers
Post by: Angiel on September 19, 2018, 06:01:44 PM
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 :).


Title: Re: Request: Help check the language workers for accuracy
Post by: Danub on September 23, 2018, 10:04:08 AM
Now, I'm assuming that you're keeping separate lists of words for singular and plural forms. If not and you're building the plurals on the fly, then you're in for a world of pain and may not be able to get a proper Romanian translation using this system, since forming plural in Romanian is irregular.
I'll update with proper code for the 2 methods (if actually possible) once I know how the system is designed.
Just to make sure I'm clear: Do you call
WithDefiniteArticle("horses", Gender.Male, true,  false) to get "the horses" ('gender' and 'plural' params define the 'string' param)
or
WithIndefiniteArticle("horse", Gender.Male, true,  false) to get the same result ('gender' and 'plural' params define the required result)
Gender doesn't apply to English, but you get the point.. If it's the second case, then you can't use it for Romanian.
Title: Re: Request: Help check the language workers for accuracy
Post by: Elevator on October 01, 2018, 07:37:15 AM
The current implementation of GenText.ToCommaList() provides incorrect punctuation for Russian language (and possibly for some other ones). In Russian there should be no comma before "and", so enumerations should look like: "Torso, neck, left shoulder and right shoulder"
Could you please add ToCommaList(IEnumerable<string> items, bool useAnd) virtual method to LanguageWorker base class? It may be called from GenText.ToCommaList() and could be overriden in subclasses.

Btw, SitePartWorker_Turrets.GetThreatsInfo() also uses "AndLower" keyed value for the same purpose. I believe this method could be refactored to use  GenText.ToCommaList() like it is done in all other contexts in the game.
Title: Re: Request: Help check the language workers for accuracy
Post by: Adirelle on November 15, 2018, 02:53:22 AM
Hi there,

About french:

I/ Pluralization

Here are the proper pluralization rules (using the first match):

1) special cases (we should probably check if these words are actually used in the translations):
1a) "bail", "corail", "émail", "gemmail", "soupirail", "travail", "vantail", "vitrail": replace "ail" by "aux",
1b) "bleu", "émeu", "landau", "lieu", "pneu", "sarrau", "bal", "banal", "fatal", "final", "festival" : append "s",
1c) "bijou", "caillou", "chou", "genou", "hibou", "joujou", "pou" : append "x",
2) words ending in "s", "x" or "z": do not change anything,
3) words ending in "al": replace "al" by "aux",
4) words ending in "au" or "eu": append "x",
5) else append "s".

II/ PostProcessing

In addition to what b606 said, ellision should be applied before word mangling.
All these rules are case-insensitive and also apply at the start of sentences, e.g. "si il" => "s'il" should means .Replace(" si il ", " s'il ").Replace("Si il", "S'il"). This would be easier to write as regular exceptions (e.g. "si il" => "s'il" becomes "\b([sS])i il" => "$1'il"). Are they allowed ?

- words "de", "le", "la" followed by a vowel: replace by "d'", "l'" and "l'" respectively.
- "si il" => "s'il"
- "que il" => "qu'il"
- "lorsque il" => "lorsqu'il"
- "que elle" => "qu'elle"
- "lorsque elle" => "lorsqu'elle"
(existing rules:)
- "de le" => "du",
- "de les" => "des",
- "de des" => "des".
Title: Re: Request: Help check the language workers for accuracy
Post by: Bacilic on January 19, 2019, 02:13:13 PM
I have already translated 80% of version v0.18.1722 and now I want to adapt it to the new version of game (v1). There have been several changes to the translations of the game so that each language can be seen in the game as natural as possible. Because the new translation method is more complex (contains definitive and indefinite articles for each genus, pronouns, adjectives, etc) I want to do the right job. Since the English language worker does not cover Greek, it is necessary to create a language worker for Greek

How do I make - setup it?
Title: Re: Request: Help check the language workers for accuracy
Post by: Harkeidos on March 03, 2019, 03:46:55 AM
How can I implement language workers in the game?

Thanks
Title: Re: Request: Help check the language workers for accuracy
Post by: Bacilic on March 08, 2019, 01:55:19 PM
Quote from: Harkeidos on March 03, 2019, 03:46:55 AM
How can I implement language workers in the game?

First of all, the Language Worker of your language(?) must be include in the game's code.
On the first page:https://ludeon.com/forums/index.php?topic=44000.0 (https://ludeon.com/forums/index.php?topic=44000.0) of this thread you will see that these following exist:
If someone of it does, you have to declare it in the code:
<LanguageInfo>
  <friendlyNameNative>Ελληνικά</friendlyNameNative>
  <friendlyNameEnglish>Greek</friendlyNameEnglish>
  <canBeTiny>true</canBeTiny>
  <languageWorkerClass>LanguageWorker_Default</languageWorkerClass>
  <credits>

in line: "<languageWorkerClass>LanguageWorker_YOUR_LANGUAGE</languageWorkerClass>" of the file "LanguageInfo.xml"

After using the appropriate keywords:https://ludeon.com/forums/index.php?topic=43979.0 (https://ludeon.com/forums/index.php?topic=43979.0) (for each language produces different results) during translation you adjust the text in your language.
Title: Re: Request: Help check the language workers for accuracy
Post by: Elevator on March 12, 2019, 08:12:16 AM
QuoteFirst of all, the LanguageWorker of your language(?) must be include in the game's code.
Before you send your class to developers you may want to test your LanguageWorker in runtime.
This can be done by creating a mod for the game.
I've created a LanguageWorker mod for Russian language, you may use it as a template for your one:
https://github.com/Elevator89/RimWorld-LanguageWorker_Russian
Title: Re: Request: Help check the language workers for accuracy
Post by: ByJacob on November 28, 2019, 02:59:51 PM
Hi developers :D
It is possibile to add Polish Language Worker to code ??

public class LanguageWorker_Polish : LanguageWorker
{
public override string WithIndefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
return str;
}

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

private interface IResolver
{
string Resolve(string[] arguments);
}

private class ReplaceResolver : IResolver
{
// ^Replace('{0}', 'jeden-jedna')^
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
{
// ^Number( {0} | '# komentarz' | '# komentarze' | '# komentarzy' | '# komentarza' )^
// ^Number( {0} | '1           | 2              | 10              | 1/2            )^
private static readonly Regex _numberRegex =
new Regex(@"(?<floor>[0-9]+)(\.(?<frac>[0-9]+))?", RegexOptions.Compiled);

public string Resolve(string[] arguments)
{
if (arguments.Length != 5)
{
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('\'');
string fraction = arguments[4].Trim('\'');

if (hasFracPart)
{
return fraction.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)
{
if (number == 1)
{
return formOne;
}

if (number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 10 || number % 100 >= 20))
{
return formSeveral;
}

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);

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

return result;
}

private static IResolver GetResolverByKeyword(string keyword)
{
switch (keyword)
{
case "Replace":
return replaceResolver;
case "Number":
return numberCaseResolver;
default:
return null;
}
}
}
Title: Re: Request: Help check the language workers for accuracy
Post by: Arczi008TV on February 27, 2020, 12:29:00 PM
Yes, add above language worker
Title: Re: Request: Help check the language workers for accuracy
Post by: b606 on February 28, 2020, 03:04:33 PM
Hi,

I noticed few glitches in the LanguageWorker for french. It needs to be modified:

1. some words do not need elision, for example "le husky" and not "l'husky" (while "l'humain" is correct).

2. for X_possessive : "sa" (female) should be replace with "son" when followed by a vowel. Ex. "sa épée" (her sword) should be "son épée"

By the way, the french language does not formally have neutral gender, so in Keyed/Grammar.xml the tag <Proits> is insufficient : is there a way to superseed it by the Strings/WordInfo/Gender/{Male,Female}.txt files so that the game gets "son" (<Prohis> or <ProitsCap>) or "sa" (<Proher> or <ProherCap>) when the word is listed in these files ?
My use case for this is in the RulePacks_*.xml where X_gender is still not effective.

3. In LanguageWorker, Pluralize fuction needs to be much more complicated, especially for invariant or compound nouns. Wouldn't it be easier to use a tag labelPlural for the ThingDef ? Ex of plurals:
"balles de revolver" (revolver bullets, "s" on the first word), "chapeaux de cowboy" (cowboy hats), "vestes renforcées" (flak jackets, get two "s"). The algorithm as it is now would then be used for things that do not have labelPlural defined.

Lastly, is there any chance to get XXX_gender work in RulePacks_* ?

Best regards


Title: Re: Request: Help check the language workers for accuracy
Post by: zerstrick on October 20, 2020, 10:44:28 PM
Hello ison, the truth is, I do not understand much about programming, but I do understand Spanish and there is the possibility of adding this to the code, it will work for both languages ​​of Spanish (Latin American and Spain)

We need plural form for indefite and definite articles

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;

if( plural )
return (gender == Gender.Female ? "unas " : "unos ") + str;

else

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;

if( plural )
return (gender == Gender.Female ? "las " : "los ") + str;

else

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;
}
}
Title: Re: Request: Help check the language workers for accuracy
Post by: ison on August 12, 2021, 04:46:05 AM
Quote from: ByJacob on November 28, 2019, 02:59:51 PM
Hi developers :D
It is possibile to add Polish Language Worker to code ??

public class LanguageWorker_Polish : LanguageWorker
{
public override string WithIndefiniteArticle(string str, Gender gender, bool plural = false, bool name = false)
{
return str;
}

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

private interface IResolver
{
string Resolve(string[] arguments);
}

private class ReplaceResolver : IResolver
{
// ^Replace('{0}', 'jeden-jedna')^
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
{
// ^Number( {0} | '# komentarz' | '# komentarze' | '# komentarzy' | '# komentarza' )^
// ^Number( {0} | '1           | 2              | 10              | 1/2            )^
private static readonly Regex _numberRegex =
new Regex(@"(?<floor>[0-9]+)(\.(?<frac>[0-9]+))?", RegexOptions.Compiled);

public string Resolve(string[] arguments)
{
if (arguments.Length != 5)
{
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('\'');
string fraction = arguments[4].Trim('\'');

if (hasFracPart)
{
return fraction.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)
{
if (number == 1)
{
return formOne;
}

if (number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 10 || number % 100 >= 20))
{
return formSeveral;
}

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);

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

return result;
}

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


Thanks for posting the LanguageWorker! However, since 1.3 we've introduced a new system which makes such custom code unnecessary. We now support "numCase" and "replace" resolvers natively and every language can use them. You could take a look at the Russian language for an example.
Title: Re: Request: Help check the language workers for accuracy
Post by: Arczi008TV on August 21, 2021, 01:32:28 PM
Quote from: ison on August 12, 2021, 04:46:05 AM
Thanks for posting the LanguageWorker! However, since 1.3 we've introduced a new system which makes such custom code unnecessary. We now support "numCase" and "replace" resolvers natively and every language can use them. You could take a look at the Russian language for an example.

numCase not working for me
(https://i.imgur.com/FrIq2LV.png)


And what about fraction & world map names? In english language worker its uses Big Letter Size
With default worker, or without:     Only first word is big

(https://i.imgur.com/PmH43Ny.png)
Title: Re: Request: Help check the language workers for accuracy
Post by: J0anJosep on September 26, 2022, 03:51:53 PM
I also tried to use numCase and I get the same error as Arczi008TV. Do I need to add something to the language worker to use numCase?

I also updated the Catalan language worker. The updated version is here (https://github.com/Ludeon/RimWorld-Catalan/blob/master/Language%20Worker.txt). Can it be added for the next RimWorld release? Thanks!