DMagic

Addon Localization Home

120 posts in this topic

Now that KSP natively supports localization we might as well try to get mods to support it wherever possible, too.

This topic will serve as a database of mods that support localization, a place where people who can provide translation help can connect with mods that need said help, and as place to discuss how to implement localization in your mod and how to work with stock localization.

 

For anyone who wants to add their mod to the list, please provide links to the primary resources in need of localization (preferably a GitHub link to the text files or folder with the files), an estimate of  how many lines and words need translating, and the languages needed.

 

Before anyone begins translating it's worth checking to see if someone else is working on a translation for that mod. Check the forum threads for that mod, and check for any recent forks on GitHub (on the top-right of the GitHub page, click the number icon next to the fork button to see who has copied the GitHub repo).

Stock Translations:

 

Localization supported; translations available:

 

 

 

Localization supported; translations needed - Links point to the primary assets in need of translating:

 

 

 

 

 

Useful links:

 

8 people like this

Share this post


Link to post
Share on other sites

Posted (edited)

I have some general notes about how stock localization works that I can put here.

All of the text files are kept in the KSP/GameData/Squad/Localization folder. Each config file uses the standard KSP config node structure:

Localization
{
	en-us
	{
		#autoLOC_7001100 = Loading...
		#autoLOC_7001101 = Adding K to Every Word...
		#autoLOC_7001102 = Adding More Boosters...
	}
}

 

Each text entry uses an identifier: #autoLOC_ followed by in id. I believe that anything can be used for an id, alphanumerics, periods, etc., but maybe not an underscore, and definitely no curly braces.

There is nothing special about the file location, you can move the files out into another folder in GameData and they will load fine.

I assume they are replaced when a different language selection is made in Steam or the KSP Store (Steam refuses to let me switch languages, so I can’t be sure).

 

For actual usage there are two different methods. One is for use in other config files, part descriptions, science results, etc.; for these you just replace the text in the existing config file with the text id.

	manufacturer = #autoLOC_501627
	description = #autoLOC_500605

or
	KerbinSrfLandedLaunchpad = #autoLOC_501259

 

 

To use the text in code, everything can be found in the KSP.Localization.Localizer class. In place of hard-coded strings you can use Localizer .Format and the string id.


	using KSP.Localization;

	String text = Localizer.Format(“#autoLOC_500605”);


The text fields also use something similar to the placeholders in string.format strings. Because config nodes use {} to open and close segments, they can’t be used in the text fields. Instead format positions are denoted with double arrows: <<1>>, also note these start with 1, instead of the 0-based index that string.format uses. I’m not sure, but I think standard string content formatting works, so you can use <<1:P1>> to convert 0.1257 to 12.6%, or <<2:N3>>  to convert 12.245436356 to 12.245. It seems that Lingoona has many different string format converters and they don't follow the same style as the standard c# string formats.

	String text = Localizer.Format(“autoLOC_600605”, new string[]
				{
					0.1256.ToString(“P1”),
					CelestialBody.displayName
				});

Or

	String text = Localizer.Format(“autoLOC_600605”, new object[]
				{
					0.1256,
					CelestialBody.displayName
				});

Or just this

	String text = Localizer.Format("autoLOC_600605, 0.1256, CelestialBody.displayName);

 

Some of this will need confirming to make sure that’s how it all works, but I think for the most part this is all correct.

Another thing to note about the stock translations is that there are a lot of them. Depending on what your mod does you may find that the required translations already exist in the included files. The files are long, but if you know what you are searching for it can be easy to find.

Edited by DMagic
Update Localizer class name
1 person likes this

Share this post


Link to post
Share on other sites

Posted (edited)

31 minutes ago, DMagic said:

I assume they are replaced when a different language selection is made in Steam or the KSP Store (Steam refuses to let me switch languages, so I can’t be sure).

Confirmed, when you change languages in Steam, it queues a ~50MB download and those files are overwritten.

Mods, of course, can't do this, since we're not distributed on Steam. We need to support all locales with a single download, unless we want to go the route of cluttering NetKAN with 5 times as many packages just for localization. So we need to 1) allow all of our locales to be loaded into memory side by side, and 2) choose between them.

Quote

I’m not sure, but I think standard string content formatting works, so you can use <<1:P1>> to convert 0.1257 to 12.6%, or <<2:N3>>  to convert 12.245436356 to 12.245.

I think the placeholder format is different, based on the Lingoona grammar module:

0LzDC5J.png

The part after the colon seems to be the field number, and the first part specifies the grammar context. The strings that get inserted into those fields can encode grammar data about themselves after a caret. E.g., check planets.cfg in en-us:

		#autoLOC_910051 = Moho^N
		#autoLOC_910035 = The Mun^N
		#autoLOC_910021 = Pol^N
		#autoLOC_910053 = The Sun^N
		#autoLOC_910025 = Tylo^N

The ^N strings mean "neuter," so Lingoona will insert "it" and "its" into certain sentences referencing these resources. Those values are in CelestialBody.displayNameGender.

Edited by HebaruSan
1 person likes this

Share this post


Link to post
Share on other sites

Posted (edited)

For SCANsat, I have an existing framework in place to handle localization. It works in broadly the same manner as stock localization, but there are some differences.

Text fields are read from config files on disk, each with an identifier, this time using simple names, rather than the #autoLOC ids. Some processing is required to convert the files from disk into text that works in code. Then the text fields are applied to wherever they are needed based on their identifier.

The first difference is that the text identifier is usually applied to Text objects in Unity, rather than in code. So each text field that supports localization has a simple tag that tells SCANsat which string to apply to it.

The other difference is in how the text is processed. It is loaded from Config Nodes in basically the same way as anything else, but it uses some Regex to match string formatters and new line breaks. 


		/// <summary>
		/// Convert string fields from the Config Node on disk to a format readable in code.
		/// 
		/// The first two Regex fields match the opening and closing arrows and are used to replace them with curly braces,
		/// the Regex is created to very specifically only match arrows found for use in string.format.
		/// 
		/// The third Regex is used to create line breaks. The Config Node loader apparently strips \n characters from values
		/// so I'm using something simple in its place.
		/// 
		/// Reflection is used to apply the Regex matches to all string fields in the object.
		/// </summary>
		public override void OnDecodeFromConfigNode()
		{
			Regex openBracket = new Regex(@"\<\<(?=\d+:?\w?\d?\>\>)");

			Regex closeBraket = new Regex(@"(?<=\{\d+:?\w?\d?)\>\>");

			Regex newLines = new Regex("NEWLINE");

			var stringFields = this.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public).Where(a => a.FieldType == typeof(string)).ToList();

			for (int i = stringFields.Count - 1; i >= 0; i--)
			{				
				FieldInfo f = stringFields[i];

				f.SetValue(this, openBracket.Replace((string)f.GetValue(this), "{"));

				f.SetValue(this, closeBraket.Replace((string)f.GetValue(this), "}"));

				f.SetValue(this, newLines.Replace((string)f.GetValue(this), Environment.NewLine));
			}
		}

 

Then, when the UI is being loaded and processed the text for the appropriate translation is applied to all of the tagged Unity Text objects.


		/// <summary>
		/// This method is used to match a string id with string in this class.
		/// 
		/// It is not particularly efficient and is only used once, during UI processing.
		/// </summary>
		/// <param name="title"></param>
		/// <returns></returns>
		public string GetStringWithName(string title)
		{
			var stringField = this.GetType()
				.GetFields(BindingFlags.Instance | BindingFlags.Public)
				.Where(a => a.FieldType == typeof(string))
				.FirstOrDefault(f => f.Name == title);

			if (stringField != null)
				return (string)stringField.GetValue(this);

			return "";
		}

 

Strings that aren’t applied directly to Text objects can easily be used by storing a static reference to the active language file. Any text that uses string formatting can be used directly in a string.format. All of this code uses lots of Linq and Reflection and so probably shouldn't be used in any manner that requires it to be repeatedly called during use. For SCANsat everything is applied during UI processing in the main menu, strings that need to be accessed later are then used by referring to the active language pack.

The most up-to-date version (which is still out-of-date :o) of all this code can be found in the dev branch of the SCANsat GitHub repo.

Edited by DMagic
2 people like this

Share this post


Link to post
Share on other sites

Good stuff!  Just one thing to add - if memory serves the autoloc_XXXXXX format was due to the fact that all those strings were pulled from KSP via a script with no real context of what it was pulling.  There is no restriction on the format of the string identifier so I'd suggest something like:

<mod_identifier>[_<sub_identfier>]_<string_identifier>

Examples:

scansat_settings_HelpAnomalies
cc_param_reachState_title

The only thing that matter is that the are globally unique - hence putting the mod name/identifier in there to prevent any clashes.

3 people like this

Share this post


Link to post
Share on other sites

Posted (edited)

So, for a very basic translation:

1) Make a MyEn-US.cfg file in my mod directory.

2) Populate this file with:

Localization //locked in, tells KSP localization module to process this file
{
	en-us //locked in, tells KSP to add the below strings to the en-us localization
	{
		MyModName = Action Groups Extended //assume this will fail, no number sign as first character
		#autoLOCMyModName = Action Groups Extended //probably also fails? I'm assuming up to the underscore is required.
		#autoLOC_MyModName = Action Groups Extended //this should work, barring all the conflicts the generic name would cause.
		#autoLOC_AGXMyModName = Action Groups Extended //a better idea, codes this string to my mod with an AGX prefix to avoid conflicts
	}
}

Then, in code (with the using Localization; line applied):

SelPartsWin = GUI.Window(673467794, SelPartsWin, SelParts, Localization.Format("#autoLOC_AGXMyModName"), AGXWinStyle);
//this is a GUI Window command using the legacy GUI where I want my mod name to display

That sound about right?

D.

 

edit: @nightingale For the "any format" working in the language config file, was that just for pulling the strings or for getting the Localization Auto Formatting to apply also?

Edited by Diazo
1 person likes this

Share this post


Link to post
Share on other sites

Posted (edited)

@Diazo Yes, except that you should be beaten over the head for suggesting its use for OnGUI. :sticktongue: There might actually be problems using OnGUI, since I'm not sure how the fonts would work. 

The UISkinManager lists NotoSans-Regular SDF as the TextMeshPro Font being used for the English version. Different fonts may be used for different translations, and the TMP Font is probably not compatible with OnGUI fonts, or with standard Unity fonts.

 

Another thing to note, since the Config Nodes use the language selection for the node with all of the text fields, I think you can include multiple translations in a single file. You would just use the same text ids for each translation and put them under different nodes, each with their respective language id.

Localization
{
	en-us
	{
		#autoLOC_7001100 = Loading...
		#autoLOC_7001101 = Adding K to Every Word...
		#autoLOC_7001102 = Adding More Boosters...
	}

	//I don't know the actual available language codes
	es
	{
		#autoLOC_7001100 = ...
	}

	cn
	{
		#autoLOC_7001100 = ...
	}
}

 

Edited by DMagic

Share this post


Link to post
Share on other sites

Posted (edited)

8 minutes ago, DMagic said:

@Diazo Yes, except that you should be beaten over the head for suggesting its use for OnGUI. :sticktongue: There might actually be problems using OnGUI, since I'm not sure how the fonts would work. 

The UISkinManager lists NotoSans as the TextMeshPro Font being used for the English version. Different fonts may be used for different translations, and the TMP Font is probably not compatible with OnGUI fonts, or with standard Unity fonts.

 

Wait, Localization.Format() isn't returning just a basic text object of C# type string?

As for the OnGUI() thing, I am moving my mods over to the new UI, but it won't be in time for the next KSP version so I'm going to have to make OnGUI and Localization work somehow to get my mods updated in a reasonable time frame.

8 minutes ago, DMagic said:

Another thing to note, since the Config Nodes use the language selection for the node with all of the text fields, I think you can include multiple translations in a single file. You would just use the same text ids for each translation and put them under different nodes, each with their respective language id.

 

I'm specifically trying to avoid multiple languages in one file for ease of use. I want to take language A from person A and language B from person B in their own separate files and not have to worry about it.

D.

Edited by Diazo

Share this post


Link to post
Share on other sites
1 minute ago, Diazo said:

Wait, Localization.Format() isn't returning just a basic text string?

No, I'm saying the font set used by the GUILayout system is very different from the fonts used by TextMeshPro, and they aren't compatible. So it's possible that certain character sets won't render correctly when using OnGUI. Standard Unity UI Text objects might also not render correctly, or they might require that modders find usable fonts. I try to use TMP objects whenever possible, but that complicates making a UI quite a bit, so it would be nice if someone could find out if there are acceptable Unity fonts that we could use.

 

Separate files should also be fine, I'm just saying that they can probably all be downloaded and loaded by KSP at the same time.

Share this post


Link to post
Share on other sites
28 minutes ago, nightingale said:

Good stuff!  Just one thing to add - if memory serves the autoloc_XXXXXX format was due to the fact that all those strings were pulled from KSP via a script with no real context of what it was pulling.  There is no restriction on the format of the string identifier so I'd suggest something like:


<mod_identifier>[_<sub_identfier>]_<string_identifier>

Examples:


scansat_settings_HelpAnomalies
cc_param_reachState_title

The only thing that matter is that the are globally unique - hence putting the mod name/identifier in there to prevent any clashes.

How does Localization.Format("cc_param_reachState_title") know I want the cfg resource rather than that literal string? Does it always parse the first parameter as a resource name?

Also, what happens if we request a string that isn't available in the current locale? Is there a way to specify a default?

1 person likes this

Share this post


Link to post
Share on other sites

Posted (edited)

Ah, you're talking about the on-screen font having issues rendering characters in the Localization.Format() text string, that makes sense.

I am intending to move to all TextMeshPro objects when I convert my mods over to the new GUI, but I don't have a firm timeline on that so I guess I'll find out how OnGUI handles localization.

D.

@HebaruSan My assumption (having not seen the code) is that Localization.Format("textString"); is the familiar ConfigNode.GetValue("textString");, just wrapped up so that it searches in the currently selected language and applies any formatting required by special characters that may be in the string.

I fully expect that:

string strObj = "textString"
Debug.Log(Localization.Format(strObj));

To be fully valid code.

And I hope there is a default as well, even something as simple as defaulting to en-us would probably be sufficient (since that is the current langauge in-game), but more options would be nice.

Edited by Diazo

Share this post


Link to post
Share on other sites

Posted (edited)

11 hours ago, HebaruSan said:

How does Localization.Format("cc_param_reachState_title") know I want the cfg resource rather than that literal string? Does it always parse the first parameter as a resource name?

Answering my own question, it seems that the logic is to treat the string as a resource, and then fall back to treating it as a literal if there is no such resource.

11 hours ago, HebaruSan said:

Also, what happens if we request a string that isn't available in the current locale? Is there a way to specify a default?

Answering my own question again, I converted to the stock API, with only en-us defined, and everything seemed to be working fine. Then I changed my Steam language to Spanish, and all the stock text was Spanish, but my mod was still using the English strings. So that seems OK too. Now to build some es-es strings, just to be sure...

Heh, I look forward to the rants about how terrible Google Translate's output is. :rolleyes:

pjrcCzW.png

Edited by HebaruSan

Share this post


Link to post
Share on other sites

I would not jump on all of this too soon because from what I gathered some of the code is not final.

1 person likes this

Share this post


Link to post
Share on other sites
7 hours ago, sarbian said:

I would not jump on all of this too soon because from what I gathered some of the code is not final.

Thanks for the warning. I'm not planning to release before 1.3 final is out, and I'm prepared to make updates to keep up with the pre-release in my private branch.

Share this post


Link to post
Share on other sites

I have a question will they add the languages in the version of consoles? Because there I can not add translation mods and although it is perfectly understandable to refer to in the tutorials it would be more comfortable to play it in a mother tongue

Share this post


Link to post
Share on other sites
On 3/21/2017 at 1:13 PM, nightingale said:

There is no restriction on the format of the string identifier so I'd suggest something like:


<mod_identifier>[_<sub_identfier>]_<string_identifier>

 

That's good to know. Though I think, assuming stock ids don't change, that it might be a good idea to keep the #autoLOC_ prefix, since it would make any localized text field instantly recognizable as such.

 

On 3/21/2017 at 1:45 PM, Diazo said:

assumption (having not seen the code) is that Localization.Format("textString"); is the familiar ConfigNode.GetValue("textString")

All of the strings are loaded into a dictionary in the Localization class. You can directly access the dictionary, but it's probably best not to.

 

On 3/22/2017 at 8:27 AM, sarbian said:

I would not jump on all of this too soon because from what I gathered some of the code is not final.

Hopefully not much will need updating here. But I intend for this thread to be as much about helping with translations as it is about actually supporting localization.

 

For mods that support localization, but are in need of translations it would help (after 1.3 is released) if authors could provide links to the text assets (presumably a config file with all of the localization fields or a folder with several such files) and an idea of what needs translating.

For instance, SCANsat has 107 lines of UI text, 4 part descriptions and about 60 science results in need of translation.

Contracts Window + will have about 30 lines of UI text, and another 10 or so lines for associated mods.

 

@Luis topete This probably isn't the right place to get information about that. Presumably KSP 1.3, along with all of the localization support, will come to consoles at some point.

2 people like this

Share this post


Link to post
Share on other sites
5 hours ago, DMagic said:

That's good to know. Though I think, assuming stock ids don't change, that it might be a good idea to keep the #autoLOC_ prefix, since it would make any localized text field instantly recognizable as such.

I'd probably want to see a comment from @TriggerAu or @JPLRepo there - I suspect that any new strings they create moving forward won't have the autoLoc prefix.

Also note, the # isn't part of the prefix - I think that's the identifier that tell the ConfigNode loading system that this is a localisation tag.

1 person likes this

Share this post


Link to post
Share on other sites

Posted (edited)

Hmm....This localization tag system (I suppose - especially having # in it) seems to have great troubles with ModuleManager configs... I wasn't able to load ChopShop without glitches even with ModuleManager itself not instaled...

As for community syntax, I think #LOC_<ModName>_<Part_name>_<Variable> looks good for parts.

For my MRR Mk1-Mk2 parts it will be something like this...

Spoiler

Localization
    {
    en-us
    {
    #LOC_ChopShop_Agency_Name = Dr. Jet's Chop Shop
    #LOC_ChopShop_Agency_Description = Alien agent undercover is performing what he calls "progressoring", selling to kerbals unusual and weird gadgets, which he made from kerbal's own rocket parts and modules.
    
    #LOC_ChopShop_TAGS_rover_manned = command control ?eva ?iva pilot rover rugged sas steer torque

    #LOC_ChopShop_MRR1_Title = Manned Rugged Rover Mk1
    #LOC_ChopShop_MRR1_Desc = Rugged Rover was so big and it's machinery was so shiny that Bill wanted to crawl inside... He managed to do that but stuck. After rover body was sawed apart to take him out, Bill decided to weld it back with higher ceiling to make rover more habitable and fit a fridge with snacks inside.
    #LOC_ChopShop_MRR2_Title = Manned Rugged Rover Mk2
    #LOC_ChopShop_MRR2_Desc = Nicer, longer, more habitable and aerodynamic version of MRR. Not looking like a brick anymore. This one can host THREE kerbals in expense of having less supplies. Still fits in 3.75m fairing.
    }
    ru
    {
    #LOC_ChopShop_Agency_Name = Нелегальная Мастерская Доктора Джета
    #LOC_ChopShop_Agency_Description = Инопланетянин под прикрытием занимается тем, что он сам называет "прогрессорством", продавая кербалам необычные и странные гаджеты, которые он собрал из частей и модулей кербальских же ракет.

    #LOC_ChopShop_TAGS_rover_manned = command control (core kerbnet probe rover sas space steer управление контроль кербнет ровер вездеход стабилизатор космос руль ?eva ?iva пилот САС
    
    #LOC_ChopShop_MRR1_Title = Пилотируемый Прочный Ровер Модель 1
    #LOC_ChopShop_MRR1_Desc = Прочный Ровер был таким большим, а механизмы внутри такими блестящими, что Биллу захотелось забраться внутрь... Он смог это сделать, но застрял. После того как корпус ровера распилили, чтобы его вытащить, Билл решил сварить его заново, но уже с высоким потолком, чтобы добавить роверу обитаемости и впихнуть туда холодильник с вкусняшками.
    #LOC_ChopShop_MRR2_Title = Пилотируемый Прочный Ровер Модель 2
    #LOC_ChopShop_MRR2_Desc = Это более симпатичная, длинная, обитаемая и аэродинамичная версия ППР. Уже не напоминает кирпич. Может вместить ТРЁХ кербалов за счёт уменьшения объёма припасов. Всё ещё помещается в обтекатель диаметром 3.75м.
    }
}

And it works! :cool: I just tested. 

Edited by Dr. Jet

Share this post


Link to post
Share on other sites
4 hours ago, nightingale said:

I'd probably want to see a comment from @TriggerAu or @JPLRepo there - I suspect that any new strings they create moving forward won't have the autoLoc prefix.

Also note, the # isn't part of the prefix - I think that's the identifier that tell the ConfigNode loading system that this is a localisation tag.

Pretty sure it will stay in that format. but we are in pre-release and anything can change. :D

1 hour ago, Dr. Jet said:

Hmm....This localization tag system (I suppose - especially having # in it) seems to have great troubles with ModuleManager configs... I wasn't able to load ChopShop without glitches even with ModuleManager itself not instaled...

As for community syntax, I think #LOC_<ModName>_<Part_name>_<Variable> looks good for parts.

For my MRR Mk1-Mk2 parts it will be something like this...

  Reveal hidden contents

And it works! :cool: I just tested. 

I believe MM is being worked on just like a lot of other mods in a pre-release.

Share this post


Link to post
Share on other sites

I'd highly recommend waiting till we have nailed down all the pieces here. We have a doc to share, but wont be till we have stopped making changes and wouldnt want anyone to feel theyve wasted time unravelling stuff that could change and then feel theyve wasted time because of it

4 people like this

Share this post


Link to post
Share on other sites

Thread has been stickied and moved into the 1.2.9 subforum.

Share this post


Link to post
Share on other sites
On ‎27‎/‎03‎/‎2017 at 0:12 AM, TriggerAu said:

I'd highly recommend waiting till we have nailed down all the pieces here. We have a doc to share, but wont be till we have stopped making changes and wouldnt want anyone to feel theyve wasted time unravelling stuff that could change and then feel theyve wasted time because of it

Well, can you at least hint at what is likely to change, and what is likely not to? I guess most mods will only need the very basic Localization.Format() for the UI, so it shouldn't be too hard to set in stone just one function in the API so we can all start adapting our mods.

Share this post


Link to post
Share on other sites
6 hours ago, Morse said:

Well, can you at least hint at what is likely to change, and what is likely not to? I guess most mods will only need the very basic Localization.Format() for the UI, so it shouldn't be too hard to set in stone just one function in the API so we can all start adapting our mods.

Sure, so one of the things that is likely to change is the class name (so Localization.Format) - which means we don't have a fixed in stone function that can be used. Thus the advice to wait.

We aren't sprinting to a release date, and we want to share as much as we can so you dont waste time, your and ours :) patience my friend

Share this post


Link to post
Share on other sites

Ok, while I wait, can you tell me what is planned for KSPedia localization? The captions inside the entries could be fetched from the *.cfg file on load (probably), but the names of the categories themselves are hardcoded into the xml file inside the asset bundle. Is there going to be a solution?

Share this post


Link to post
Share on other sites

Has someone got a list of which languages are currently supported?  As modders, even if we rely on "quality" google translate, we will probably need to at least populate the set of languages for anything we use localization for.  

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now