Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Topics - ison

#1
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;
}
}
#2
Translations / New keyed translations format
September 04, 2018, 08:06:39 AM
New keyed translations format
(this post describes a system which will be used in the upcoming unstable build)

We highgly recommend using the translation cleaning tool before changing the keyed translations! This will update all "EN: " comments with the up-to-date English texts.

We've changed the keyed translations format a bit, but it's back-compatible which means everything should work the same as before even if you don't change anything.

Most keyed translations now have extra symbols you can use, like definite and indefinite forms of the item's label. You can access it by writing, for example {0_X}, where X is the name of the sub-symbol. You can find the list of all available sub-symbols at the end of this post.

For example, if {0} refers to a person, you can now write:
{0_definite} took {0_possessive} backpack.
which will be resolved to: John took his backpack.

This flexibility may or may not be useful in your case, depending on how the sentence looks like in your language. Sometimes it's enough to use exactly the same symbols as in English, and sometimes you may want to use more.

Some symbols now have a name, but using numbers is still valid. For example, an English translation may contain text like this: {PAWN_definite}.

There used to be a problem with definite and indefinite articles in some languages. This problem is now fixed for the following languages: German, French, Korean, Russian, Italian, Spanish. So if you used to use e.g. "Un(e)" in French, you now may want to change it to {0_indefinite} (0 is an example here) which should resolve to either "un" or "une" based on the pawn's gender. If your language has definite and indefinite articles but they don't work, then please let us know so we can implement them.

Some languages use different words for each gender. In English this is solved by using {0_pronoun}, {0_objective}, and {0_possessive}. Some languages have many more pronouns though. This can now be solved by using a conditional expression like this: {PAWN_gender ? A : B}. This will resolve to either A or B based on the pawn's gender. If your language uses 3 genders, you can use {PAWN_gender ? A : B : C} (A for male, B for female, and C for neuter). It's possible to use this syntax to resolve indefinite and definite articles, e.g. in French {PAWN_gender ? un : une}, however we don't recommend doing this. In this case it's better to use {PAWN_indefinite}.

Our system can easily tell the gender of a pawn, but it can't tell the gender of any other object. This means that if you want to use a gender conditional expression on, for example, items, then you need to provide some lookup tables by creating the following files in your language folder:
WordInfo/Gender/Male.txt
WordInfo/Gender/Female.txt
WordInfo/Gender/Neuter.txt
and add one word per line to each file, e.g.: (Male.txt)
sword
computer
keyboard


then if an object whose label is "sword" is passed, its _gender will be male.

This makes it possible to do this:
{THING_label} is {THING_gender ? beautiful1 : beautiful2}
where "beautiful" is different based on the THING's gender (which happens in some languages).

Note that we now use {} everywhere instead of []. The RulePackDef system (used by art descriptions and name generators) still uses [] though. Even though the RulePackDefs system works similarly they are 2 separate systems.

List of available symbols for Pawns:
{0}: John, a cat, a dog (short name if possible, otherwise indefinite form of the pawn kind)
{0_nameFull}: John Doe, a cat, a dog (full name if possible, otherwise indefinite form of the pawn kind)
{0_nameFullDef}: John Doe, the cat, the dog (full name if possible, otherwise definite form of the pawn kind)
{0_label}: John, Constructor; cat; dog (label (name + title))
{0_labelShort}: John, cat, dog (short name if possible, otherwise kind label)
{0_definite}: John, the cat, the dog (short name if possible, otherwise definite form of the pawn kind)
{0_nameDef}: John, the cat, the dog (short name if possible, otherwise definite form of the pawn kind)
{0_indefinite}: John, a cat, a dog (short name if possible, otherwise indefinite form of the pawn kind)
{0_nameIndef}: John, a cat, a dog (short name if possible, otherwise indefinite form of the pawn kind)
{0_pronoun}: he/she
{0_possessive}: his/her
{0_objective}: him/her
{0_factionName}: Some Community (faction name)
{0_factionPawnSingular}: colonist (faction member label)
{0_factionPawnSingularDef}: the colonist (faction member label (definite))
{0_factionPawnSingularIndef}: a colonist (faction member label (indefinite))
{0_factionPawnPlural}: colonists (faction members label)
{0_factionPawnPluralDef}: the colonists (faction members label, definite)
{0_factionPawnPluralIndef}: colonists (faction members label, indefinite)
{0_kind}: human, cat, dog (kind label)
{0_kindDef}: the human, the cat, the dog (kind label (definite))
{0_kindIndef}: a human, a cat, a dog (kind label (indefinite))
{0_kindPlural}: humans, cats, dogs (kind label (plural))
{0_kindPluralDef}: the humans, the cats, the dogs (kind label (plural), definite)
{0_kindPluralIndef}: humans, cats, dogs (kind label (plural), indefinite)
{0_kindBase}: human, cat, dog, deer (instead of buck/doe) (base genderless kind label)
{0_kindBaseDef}: the human, the cat, the dog, the deer (base genderless kind label (definite))
{0_kindBaseIndef}: a human, a cat, a dog, a deer (base genderless kind label (indefinite))
{0_kindBasePlural}: humans, cats, dogs, deer (base genderless kind label (plural))
{0_kindBasePluralDef}: the humans, the cats, the dogs, the deer (base genderless kind label (plural), definite)
{0_kindBasePluralIndef}: humans, cats, dogs, deer (base genderless kind label (plural), indefinite)
{0_lifeStage}: teenager, baby, adult (life stage label)
{0_lifeStageDef}: the teenager, the baby, the adult (life stage label (definite))
{0_lifeStageIndef}: a teenager, a baby, an adult (life stage label (indefinite))
{0_lifeStageAdjective}: teenage, baby, adult
{0_title}: constructor (pawn title)
{0_titleDef}: the constructor (pawn title (definite))
{0_titleIndef}: a constructor (pawn title (indefinite))
{0_gender}: male, female (pawn gender)
{0_gender ? A : B}: A if male or neuter, B if female
{0_gender ? A : B : C}: A if male, B if female, C if neuter
{0_humanlike ? A : B}: A if humanlike, B if not

How to contribute
RimWorld 1.0 Translation Improvements
New translation cleaner tool
More minor 1.0 changes + info about LanguageWorkers
Request: Help check the language workers for accuracy

[attachment deleted due to age]
#3
Just so you know, since it causes problems in other languages, we've added 2 new keyed translations: MaleAnimal and FemaleAnimal which are "male" and "female" labels used when the pawn is an animal.

And while we're at it, I'd like to remind you that it's possible to define a custom LanguageWorker for a language. This is simply a piece of C# code which handles complex language specific string conversions. Currently only English, Russian, and Korean use it.

The available methods are:
WithIndefiniteArticle(): e.g. "an elephant"
WithDefiniteArticle(): e.g. "the elephant"
OrdinalNumber(): e.g. 1st 7th 15th
PostProcessed(): returns post-processed text generated by RulePacks
ToTitleCase(): converts "some text" to "Some Text"
Pluralize(): returns the plural form of the given word
PostProcessedBackstoryDescription(): returns post-processed backstory description
PostProcessedKeyedTranslation(): returns post-processed keyed translation (i.e. any non-def-injected translation)

You can override any of these methods. You can also suggest adding more if needed.

How to contribute
New keyed translations format
RimWorld 1.0 Translation Improvements
New translation cleaner tool
Request: Help check the language workers for accuracy
#4
RimWorld 1.0 Translation Improvements

In this thread we'd like to explain three new important features which have been added to the translation system in RimWorld 1.0 and which will hopefully make translating RimWorld easier.

1. Translation report

It's now possible to generate a translation report for the currently selected, non-English language. All you need to do is press the "Generate translation report" button in the main menu.

Translation report generator analyzes the translation files for the currently selected language and reports all missing translations, unnecessary translations, XML parsing errors, def-injection errors, and more. In previous versions some of those errors used to be shown in the message log, but due to poor formatting it was much more difficult to read and then fix the problem.

Translation report consists of 13 sections:


  • General load errors: XML parsing errors, incorrectly named files, duplicate keyed translations, and other unexpected errors.
  • Def-injected translations load errors: all errors caused by def-injections like using non-existent defs, using a field which doesn't exist, duplicate def-injections, def-injections parsing errors, and more.
  • Backstories load errors: all errors caused by invalid backstories data.
  • Missing keyed translations
  • Missing def-injections
  • Missing backstory translations
  • Unnecessary def-injections
  • Def-injected translations using old, renamed defs: this section reports all def-injections which use obsolete def names
  • Argument count mismatches: warning when the number of arguments in English is different (e.g. English text uses {0}, {1} but the translation only has {0}). In some cases it's not an error - translations are free to omit some of the arguments if they don't need it.
  • Unnecessary keyed translations
  • Keyed translations matching English: these are not necessarily errors, sometimes translated text can be the same in English.
  • Backstories matching english: most likely an error.
  • Def-injections syntax suggestions: suggestions to use named list indexes (see feature number 3).

2. Full-list translations

We realize that automatically generated text is one of the most difficult things to translate. In our case such text is generated by RulePackDefs. Before 1.0 translating them was cumbersome because all lists had to have exactly the same number of elements as in English (and some RulePackDefs have 30 or even more items!). The problem with automatically generated text is that different languages have different number of synonyms and, in general, different ways to express the same meaning. This means that translations should be able to add or remove items from RulePackDefs. Different languages can have completely different RulePackDefs.

From now on it's possible to use a so called full-list translation. This means that instead of this:
<TalelessImages.rulePack.rulesStrings.0>image->einem [outlander] der von [quantity_adjphrase] [outlander]n umstellt ist</TalelessImages.rulePack.rulesStrings.0>
<TalelessImages.rulePack.rulesStrings.1>image->einem [outlander] der mit mehreren [outlander]n eine Partie [game] spielt</TalelessImages.rulePack.rulesStrings.1>
<TalelessImages.rulePack.rulesStrings.2>image->einem [outlander] mit [weapon] [subject_desc]</TalelessImages.rulePack.rulesStrings.2>
<TalelessImages.rulePack.rulesStrings.3>image->einem einsamen [outlander]</TalelessImages.rulePack.rulesStrings.3>


It's now possible to do this:
<TalelessImages.rulePack.rulesStrings>
  <li>image->einem [outlander] der von [quantity_adjphrase] [outlander]n umstellt ist</li>
  <li>image->einem [outlander] der mit mehreren [outlander]n eine Partie [game] spielt</li>
  <li>image->einem [outlander] mit [weapon] [subject_desc]</li>
  <li>image->einem einsamen [outlander]</li>
</TalelessImages.rulePack.rulesStrings>


with as many <li> nodes as needed.

This conversion was done automatically. We've run a script to update all translations and make them use this new syntax, so you don't have to worry about it.

We've also added a new variable to rulePack called rulesFiles. You can now add more file rules like this:
<TalelessImages.rulePack.rulesFiles>
  <li>mynewkeyword->Words/Nouns/MyNewKeyword</li>
  <li>somethingelse->Words/Nouns/Something</li>
</TalelessImages.rulePack.rulesFiles>


Please note that it will override all file rules defined in the English language!

3. Named indexes for lists

It's now possible to refer to the elements of a list by using their unique name instead of their index in def-injections.

A few examples:
instead of:
<NaturalMood.degreeDatas.2.label>translation</NaturalMood.degreeDatas.2.label>
it's now possible to do this:
<NaturalMood.degreeDatas.pessimist.label>translation</NaturalMood.degreeDatas.pessimist.label>

instead of:
<NeedFood.stages.5.description>translation</NeedFood.stages.5.description>
it's now possible to do this:
<NeedFood.stages.advanced_starvation.description>translation</NeedFood.stages.advanced_starvation.description>

instead of:
<Turret_MiniTurret.comps.5.fuelLabel>translation</Turret_MiniTurret.comps.5.fuelLabel>
it's now possible to do this:
<Turret_MiniTurret.comps.CompRefuelable.fuelLabel>translation</Turret_MiniTurret.comps.CompRefuelable.fuelLabel>

This solves 2 problems:

  • Translators don't know what exactly they're translating. Translating comps.0 or comps.1 doesn't say much about the component and it's unintuitive.
  • All translations break when a new element is added, especially at the beginning of the list. And what's worse is that there's no way to tell if the translation is still correct or if it's now invalid because it refers to a completely different element.

The second problem is the reason why we didn't convert all files automatically. We realize that updating all list elements will be a difficult task, so it's completely optional - using indexes is still valid. It's a good opportunity to recheck all list translations and make sure they're correct while making translations use this new syntax. The "Def-injections syntax suggestions" section in the translation report may be particularly helpful here.

4. Translation cleaner tool

We've added a very useful tool which cleanes up all translation files, adds missing translations, and more.
More info here: https://ludeon.com/forums/index.php?topic=42587.0


And that's it. We hope this will make translating RimWorld easier.

How to contribute
New keyed translations format
New translation cleaner tool
More minor 1.0 changes + info about LanguageWorkers
Request: Help check the language workers for accuracy

[attachment deleted due to age]
#5
General Discussion / Upload your colony for the trailer
November 16, 2017, 11:37:56 AM
Hello everybody,

we are making a trailer for RimWorld and we need your help. We would like to show some great, big colonies in the video so if you are interested, we would like you to upload the savefile of your colony in this thread.

Of course the more impressive the colony the better, however we would also like to see some big but not super-rich colonies, so if you have been playing for a while, it would be great if you could upload your colony!

It is important for the colony to be purely vanilla (no mods ever used) and from Alpha 18.

It is also important to note that uploading a colony in this thread or providing a link to download your colony means that you give us permission to use it in any way, including using it in the video and then using the video in any way. We would like everyone to write at least a short sentence that you agree to give us such permission.

Thanks!
#6
Hello,

we've received several reports from players using Linux about problems with zooming in/out in A18 unstable.

I'd like to know if anyone is using Linux and the scrolling is working fine.

Thanks