Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: mockingbirdnest/Principia
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 5cfb589822c2
Choose a base ref
...
head repository: mockingbirdnest/Principia
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 1bd3d975eaa6
Choose a head ref
  • 7 commits
  • 7 files changed
  • 1 contributor

Commits on Aug 16, 2021

  1. 3

    eggrobin committed Aug 16, 2021
    Copy the full SHA
    ef39dfc View commit details

Commits on Aug 17, 2021

  1. merge

    eggrobin committed Aug 17, 2021
    Copy the full SHA
    9d3c899 View commit details
  2. not yet

    eggrobin committed Aug 17, 2021
    Copy the full SHA
    2c76c2b View commit details

Commits on Aug 18, 2021

  1. after pleroy’s review

    eggrobin committed Aug 18, 2021
    Copy the full SHA
    be2d7d1 View commit details

Commits on Aug 19, 2021

  1. ^N and ^n, not ^d

    eggrobin committed Aug 19, 2021
    Copy the full SHA
    9dad5ab View commit details
  2. Copy the full SHA
    d8b0bf1 View commit details
  3. Merge pull request #3101 from eggrobin/fr

    Improve translation malleability
    eggrobin authored Aug 19, 2021
    Copy the full SHA
    1bd3d97 View commit details
16 changes: 10 additions & 6 deletions ksp_plugin_adapter/localization/celestial_strings.cfg
Original file line number Diff line number Diff line change
@@ -20,15 +20,18 @@
// “However, the argument of periapsis increases approximately 0.9° per
// orbit, sending the latitude of perijove further from the equator.”
// in [KJL08]. We follow this usage.
#Principia_OrbitAnalyser_OrbitDescription_Primary(Sun).en-us = heliocentric
#Principia_OrbitAnalyser_OrbitDescription_Primary(Moon).en-us = lunar
#Principia_OrbitAnalyser_OrbitDescription_SynchronousPrimary(Earth).en-us = geosynchronous
#Principia_OrbitAnalyser_OrbitDescription_StationaryPrimary(Earth).en-us = geostationary

// <<3>> is the sequence of properties, ending with a space.
#Principia_OrbitAnalyser_OrbitDescription(Sun).en-us = <<3>>heliocentric orbit
#Principia_OrbitAnalyser_OrbitDescription(Moon).en-us = <<3>>lunar orbit
#Principia_OrbitAnalyser_OrbitDescription_Synchronous(Earth).en-us = <<3>>geosynchronous orbit
#Principia_OrbitAnalyser_OrbitDescription_Stationary(Earth).en-us = geostationary orbit

#Principia_ReferenceFrameSelector_Name_BodyCentredNonRotating(Sun).en-us = Heliocentric Inertial
#Principia_ReferenceFrameSelector_Abbreviation_BodyCentredNonRotating(Sun).en-us = HCI
#Principia_ReferenceFrameSelector_Name_BodyCentredParentDirection(Earth,Sun).en-us = Geocentric Solar Ecliptic
#Principia_ReferenceFrameSelector_Abbreviation_BodyCentredParentDirection(Earth,Sun).en-us = GSE
#Principia_ReferenceFrameSelector_Description_BodyCentredParentDirection(Earth,Sun).en-us = The centre of <<1>> is fixed.\nThe ecliptic (the plane of its orbit around <<2>>) is horizontal.\nThe reference frame rotates with the orbit, fixing the direction of <<2>>.
#Principia_ReferenceFrameSelector_Description_BodyCentredParentDirection(Earth,Sun).en-us = The centre of <<A:1>> is fixed.\nThe ecliptic (the plane of its orbit around <<A:3>) is horizontal.\nThe reference frame rotates with the orbit, fixing the direction of <<A:3>>.
// Note that JSE is attested for Jupiter, but the more generic JSO is more
// modern (the former is common in the context of Galileo, the latter in the
// context of Juno).
@@ -78,6 +81,7 @@
#Principia_ReferenceFrameSelector_Name_BodySurface(Earth).zh-cn = 地心地固
#Principia_ReferenceFrameSelector_Name_BodySurface(Moon).zh-cn = 月心月固
#Principia_ReferenceFrameSelector_Name_BodyCentredParentDirection(Earth,Sun).zh-cn = 地心太阳黄道
#Principia_ReferenceFrameSelector_Description_BodyCentredParentDirection(Earth,Sun).zh-cn = <<1>>的质心已固定,\n水平面为黄道(<<1>>绕<<2>>的轨道面)。\n这个参考系跟随轨道旋转,面向着<<2>>的方位不变。
#Principia_ReferenceFrameSelector_NavballName_BodyCentredParentDirection(Earth,Sun).zh-cn = 地心太阳黄道
#Principia_ReferenceFrameSelector_Description_BodyCentredParentDirection(Earth,Sun).zh-cn = <<1>>的质心已固定,\n水平面为黄道(<<1>>绕<<3>>的轨道面)。\n这个参考系跟随轨道旋转,面向着<<3>>的方位不变。
}
}
74 changes: 39 additions & 35 deletions ksp_plugin_adapter/localization/en-us.cfg
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ Localization {
#Principia_MainWindow_LoggingSettings = Logging Settings
#Principia_MainWindow_KspFeature_DisplayPatchedConics = Display patched conics (do not use for flight planning!)
#Principia_MainWindow_TargetCelestial_Select = Select target celestial...
#Principia_MainWindow_TargetCelestial_Name = Target: <<1>> // <<1>> celestial name
#Principia_MainWindow_TargetCelestial_Name = Target: <<A:1>>
#Principia_MainWindow_TargetCelestial_Clear = Clear
#Principia_MainWindow_LoggingSettings_VerboseLevel = Verbose level:
#Principia_MainWindow_LoggingSettings_LogOption = Log
@@ -41,34 +41,42 @@ Localization {
#Principia_SpeedDisplayText = <<1>> m/s // <<1>>: active_vessel_velocity.magnitude.ToString("F1").

// ReferenceFrameSelector
// <<1>> and <<2>> are the names of the relevant celestial bodies without articles, <<2>> being the parent of <<1>>.

// For the names, abbreviations, and descriptions, except the description of
// the target frame, <<12>> and <<34>> are the names and initials of the relevant
// celestial bodies, <<34>> being the parent of <<12>>.
// Odd: names (Sun^n, Moon^n, Saturn^N, Terre^f, Vénus^F, 火星),
// Even: initials if applicable (S, M, S, T, V, 火星).

#Principia_ReferenceFrameSelector_Name_Target = Target-<<1>>-Orbit
#Principia_ReferenceFrameSelector_Name_BodyCentredNonRotating = <<1>>-Centred Inertial
#Principia_ReferenceFrameSelector_Name_BodyCentredParentDirection = <<1>>-<<2>>-Orbit
#Principia_ReferenceFrameSelector_Name_BodyCentredParentDirection = <<1>>-<<3>>-Orbit
#Principia_ReferenceFrameSelector_Name_BodySurface = <<1>>-Centred <<1>>-Fixed

// <<1>> and <<2>> are the first letters of the relevant celestial bodies, <<2>> being the parent of <<1>>.
#Principia_ReferenceFrameSelector_Abbreviation_Target.en-us = Tgt <<1>>O
#Principia_ReferenceFrameSelector_Abbreviation_BodyCentredNonRotating.en-us = <<1>>CI
#Principia_ReferenceFrameSelector_Abbreviation_BodyCentredParentDirection.en-us = <<1>><<2>>O
#Principia_ReferenceFrameSelector_Abbreviation_BodySurface.en-us = <<1>>C<<1>>F
#Principia_ReferenceFrameSelector_Abbreviation_Target.en-us = Tgt <<2>>O
#Principia_ReferenceFrameSelector_Abbreviation_BodyCentredNonRotating.en-us = <<2>>CI
#Principia_ReferenceFrameSelector_Abbreviation_BodyCentredParentDirection.en-us = <<2>><<4>>O
#Principia_ReferenceFrameSelector_Abbreviation_BodySurface.en-us = <<2>>C<<2>>F

// <<1>> is the full name of the reference frame, <<2>> is its abbreviation.
#Principia_ReferenceFrameSelector_Description_Heading = <<1>> (<<2>>) reference frame:
// <<1>> and <<2>> are the names of the relevant bodies, with articles if appropriate, <<2>> being the parent of <<1>>.
// For the target frame, <<1>> is the name of the target vessel.
#Principia_ReferenceFrameSelector_Description_Target = The target vessel, <<1>>, is fixed.\nThe plane of its orbit around <<2>> is horizontal.\nThe reference frame rotates with the orbit, fixing the direction of <<2>>.
#Principia_ReferenceFrameSelector_Description_BodyCentredNonRotating = The centre of <<1>> is fixed.\nIts equator is horizontal.\nThe reference frame does not rotate.
#Principia_ReferenceFrameSelector_Description_BodyCentredParentDirection = The centre of <<1>> is fixed.\nThe plane of its orbit around <<2>> is horizontal.\nThe reference frame rotates with the orbit, fixing the direction of <<2>>.
#Principia_ReferenceFrameSelector_Description_BodySurface = <<1C>> is fixed.\nIts equator is horizontal.\nThe reference frame rotates with <<1>>.
#Principia_ReferenceFrameSelector_ReferencePlane_Centred = equator of <<1>> // <<1>> centred body

// <<1>> is the name of the target vessel, <<2>> is the name of its primary.
#Principia_ReferenceFrameSelector_Description_Target = The target vessel, <<1>>, is fixed.\nThe plane of its orbit around <<A:2>> is horizontal.\nThe reference frame rotates with the orbit, fixing the direction of <<A:2>>.

#Principia_ReferenceFrameSelector_Description_BodyCentredNonRotating = The centre of <<A:1>> is fixed.\nIts equator is horizontal.\nThe reference frame does not rotate.
#Principia_ReferenceFrameSelector_Description_BodyCentredParentDirection = The centre of <<A:1>> is fixed.\nThe plane of its orbit around <<A:3>> is horizontal.\nThe reference frame rotates with the orbit, fixing the direction of <<A:3>>.
#Principia_ReferenceFrameSelector_Description_BodySurface = <<AC:1>> is fixed.\nIts equator is horizontal.\nThe reference frame rotates with <<A:1>>.

#Principia_ReferenceFrameSelector_ReferencePlane_Centred = equator of <<A:1>> // <<1>> centred body
#Principia_ReferenceFrameSelector_ReferencePlane_Secondary_Target = the target
#Principia_ReferenceFrameSelector_ReferencePlane = orbit of <<1>> around <<2>> // <<1>> secondary body <<2>> centred body
#Principia_ReferenceFrameSelector_ReferencePlane = orbit of <<A:1>> around <<A:2>> // <<1>> secondary body <<2>> centred body
#Principia_ReferenceFrameSelector_ToggleButton = <<1>> selection (<<2>>)... // <<1>> plotting <<2>> selected frame
#Principia_ReferenceFrameSelector_Title = <<1>> selection (<<2>>) // <<1>> plotting <<2>> selected frame
// <<1>> is the full name of the reference frame, <<2>> is the central body.
#Principia_ReferenceFrameSelector_Tooltip_BodyCentredNonRotating = <<1>> reference frame: for perturbed Keplerian trajectories around <<2>>.
#Principia_ReferenceFrameSelector_Tooltip_BodySurface = <<1>> reference frame: for operations involving the surface of <<2>>: landings, remote sensing, imaging, ground communications, etc.
#Principia_ReferenceFrameSelector_Tooltip_BodyCentredParentDirection = <<1>> reference frame: for flybys of <<2>>, transfers to or from it.
#Principia_ReferenceFrameSelector_Tooltip_BodyCentredNonRotating = <<1>> reference frame: for perturbed Keplerian trajectories around <<A:2>>.
#Principia_ReferenceFrameSelector_Tooltip_BodySurface = <<1>> reference frame: for operations involving the surface of <<A:2>>: landings, remote sensing, imaging, ground communications, etc.
#Principia_ReferenceFrameSelector_Tooltip_BodyCentredParentDirection = <<1>> reference frame: for flybys of <<A:2>>, transfers to or from it.
#Principia_ReferenceFrameSelector_Tooltip_Target = <<1>> reference frame: for intercepts or rendez-vous with the target vessel.
#Principia_ReferenceFrameSelector_Target = Target: <<1>>
#Principia_ReferenceFrameSelector_Pin = Pin
@@ -80,9 +88,9 @@ Localization {
#Principia_OrbitAnalyser_Duration_GroundTrackCycles = \u0020(<<1>> ground track cycles) // <<1>> ground_track_cycles.FormatN(0)
#Principia_OrbitAnalyser_Duration_Revolutions = <<1>> sidereal revolutions\n<<2>> nodal revolutions<<3>>\n<<4>> anomalistic revolutions // <<3>> duration_in_ground_track_cycles
#Principia_OrbitAnalyser_Warning_NoElements1 = could not determine elements; mission duration may be shorter than a revolution, or trajectory may not be gravitationally bound.
#Principia_OrbitAnalyser_Warning_NoElements2 = could not determine elements; mission duration may be shorter than a revolution, or trajectory may not be gravitationally bound to <<1>>. // <<1>> primary.NameWithArticle()
#Principia_OrbitAnalyser_Warning_NoElements2 = could not determine elements; mission duration may be shorter than a revolution, or trajectory may not be gravitationally bound to <<A:1>>. // <<1>> primary.NameWithArticle()
#Principia_OrbitAnalyser_Warning_NoPrimary = <<1>> is not gravitationally bound over <<2>> // <<2>>mission_duration.FormatDuration <<1>> vessel name
#Principia_OrbitAnalyser_AnalysisDescription = Orbit of <<1>> with respect to <<2>> over <<3>>:\n<<4>> // <<1>> vessel name <<2>> centre body <<3>> time <<4>> result
#Principia_OrbitAnalyser_AnalysisDescription = Orbit of <<1>> with respect to <<A:2>> over <<3>>:\n<<4>> // <<1>> vessel name <<2>> centre body <<3>> time <<4>> result
#Principia_OrbitAnalyser_OrbitDescription_Circular = circular\u0020
#Principia_OrbitAnalyser_OrbitDescription_HighlyElliptical = highly elliptical\u0020
#Principia_OrbitAnalyser_OrbitDescription_Equatorial = equatorial\u0020
@@ -91,11 +99,9 @@ Localization {
#Principia_OrbitAnalyser_OrbitDescription_Synchronous = synchronous\u0020
#Principia_OrbitAnalyser_OrbitDescription_Stationary = stationary\u0020
#Principia_OrbitAnalyser_OrbitDescription_Semisynchronous = semisynch.\u0020
// <<1>> is the name of the primary without an article.
#Principia_OrbitAnalyser_OrbitDescription_Primary = <<1>>
// <<1>> is the sequence of properties, ending with a space.
// <<2>> is the Primary string above.
#Principia_OrbitAnalyser_OrbitDescription = <<1>><<2>> orbit
// <<12>> is the primary, in two forms as above.
// <<3>> is the sequence of properties, ending with a space.
#Principia_OrbitAnalyser_OrbitDescription = <<3>><<1>> orbit
#Principia_OrbitAnalyser_Elements_LowestAltitude = Lowest altitude
#Principia_OrbitAnalyser_Warning_Collision = collision
#Principia_OrbitAnalyser_Warning_CollisionRisk = collision risk
@@ -213,19 +219,17 @@ Localization {
// MapNodePool
#Principia_MapNode_Planned = Planned
#Principia_MapNode_Predicted = Predicted
// For the apsides, <<1>> is the name of the celestial without an article.
#Principia_MapNode_Periapsis = <<1>> Periapsis
#Principia_MapNode_Apoapsis = <<1>> Apoapsis
// <<1>> is the source (predicted or planned).
// <<2>> is the apsis (from the strings above).
// <<3>> is the altitude in metres.
#Principia_MapNode_ApsisHeader = <<1>> <<2>> :\n<<3>> m
// For the apsides, <<12>> is the name of the relevant celestial (in two forms as above).
// <<3>> is the source (predicted or planned).
// <<4>> is the altitude in metres.
#Principia_MapNode_PeriapsisHeader = <<3>> <<1>> Periapsis:\n<<4>> m
#Principia_MapNode_ApoapsisHeader = <<3>> <<1>> Apoapsis:\n<<4>> m
#Principia_MapNode_ApsisCaptionLine2 = <<1>> m/s // <<1>>: speed.FormatN(0).
#Principia_MapNode_AscendingNode = Ascending Node
#Principia_MapNode_DescendingNode = Descending Node
#Principia_MapNode_NodeHeader = <<1>> <<2>> :\n<<3>> // <<1>>: source; <<2>>: node_name; <<3>>: plane.
#Principia_MapNode_NodeHeader = <<1>> <<2>>:\n<<3>> // <<1>>: source; <<2>>: node_name; <<3>>: plane.
#Principia_MapNode_NodeCaptionLine2 = <<1>> m/s // <<1>>: properties.velocity.z.FormatN(0)
#Principia_MapNode_ApproachHeader = <<1>> Target Approach : <<2>> m // <<1>>: source, <<2>>: separation.FormatN(0).
#Principia_MapNode_ApproachHeader = <<1>> Target Approach: <<2>> m // <<1>>: source, <<2>>: separation.FormatN(0).
#Principia_MapNode_ApproachCaptionLine2 = <<1>> m/s // <<1>>: speed.FormatN(0).
#Principia_MapNode_ImpactHeader = <<1>> <<2>> Impact // <<1>>: source, <<2>>: celestial.name.
}
43 changes: 25 additions & 18 deletions ksp_plugin_adapter/localization/zh-cn.cfg
Original file line number Diff line number Diff line change
@@ -41,23 +41,34 @@ Localization {
#Principia_SpeedDisplayText = <<1>> m/s // <<1>>: active_vessel_velocity.magnitude.ToString("F1").

// ReferenceFrameSelector


// For the names, abbreviations, and descriptions, except the description of
// the target frame, <<12>> and <<34>> are the names and initials of the relevant
// celestial bodies, <<34>> being the parent of <<12>>.
// Odd: names (Sun^n, Moon^n, Saturn^N, Terre^f, Vénus^F, 火星),
// Even: initials if applicable (S, M, S, T, V, 火星).
#Principia_ReferenceFrameSelector_Name_Target = 目标<<1>>轨道
#Principia_ReferenceFrameSelector_Name_BodyCentredNonRotating = <<1>>质心惯性
// The code will replace the U+200B (ZWSP) with a hyphen if both celestials have non-CJK names.
#Principia_ReferenceFrameSelector_Name_BodyCentredParentDirection = <<1>>\u200B<<2>>轨道
#Principia_ReferenceFrameSelector_Name_BodyCentredParentDirection = <<1>>\u200B<<3>>轨道
#Principia_ReferenceFrameSelector_Name_BodySurface = <<1>>体固

#Principia_ReferenceFrameSelector_NavballName_BodyCentredParentDirection.zh-cn = <<2>><<4>>轨道

#Principia_ReferenceFrameSelector_SelectorText_Target = 轨道系
#Principia_ReferenceFrameSelector_SelectorText_BodyCentredNonRotating = 惯性系
#Principia_ReferenceFrameSelector_SelectorText_BodyCentredParentDirection = 轨道系
#Principia_ReferenceFrameSelector_SelectorText_BodySurface = 体固系
// <<1>> is the full name of the reference frame, <<2>> is its abbreviation.

#Principia_ReferenceFrameSelector_Description_Heading = <<1>>参考系:
// <<1>> and <<2>> are the names of the relevant bodies, with articles if appropriate, <<2>> being the parent of <<1>>.
// For the target frame, <<1>> is the name of the target vessel.
// <<1>> is the name of the target vessel, <<2>> is the name of its primary.
#Principia_ReferenceFrameSelector_Description_Target = 目标载具<<1>>已固定,\n水平面为绕着<<2>>的轨道。\n这个参考系跟随轨道旋转,面向着<<2>>的方位不变。

#Principia_ReferenceFrameSelector_Description_BodyCentredNonRotating = <<1>>的质心已固定,\n水平面为它的赤道。\n这个参考系不旋转。
#Principia_ReferenceFrameSelector_Description_BodyCentredParentDirection = <<1>>的质心已固定,\n水平面为绕着<<2>>的轨道。\n这个参考系跟随轨道旋转,面向着<<2>>的方位不变。
#Principia_ReferenceFrameSelector_Description_BodySurface = <<1>>已固定,\n水平面为它的赤道。\n这个参考系与<<1>>共同旋转。 // <<1>>centred body name
#Principia_ReferenceFrameSelector_Description_BodyCentredParentDirection = <<1>>的质心已固定,\n水平面为绕着<<3>>的轨道。\n这个参考系跟随轨道旋转,面向着<<3>>的方位不变。
#Principia_ReferenceFrameSelector_Description_BodySurface = <<1>>已固定,\n水平面为它的赤道。\n这个参考系与<<1>>共同旋转。

#Principia_ReferenceFrameSelector_ReferencePlane_Centred = <<1>>的赤道 // <<1>> centred body
#Principia_ReferenceFrameSelector_ReferencePlane_Secondary_Target = 目标
#Principia_ReferenceFrameSelector_ReferencePlane = <<1>>绕<<2>>的轨道 // <<1>> secondary body <<2>> centred body
@@ -91,11 +102,9 @@ Localization {
#Principia_OrbitAnalyser_OrbitDescription_Stationary = 静止
// See, e.g., ITU-R-HDB-55 https://www.itu.int/pub/R-HDB-55.
#Principia_OrbitAnalyser_OrbitDescription_Semisynchronous = 半同步
// <<1>> is the name of the primary.
#Principia_OrbitAnalyser_OrbitDescription_Primary = <<1>>
// <<1>> is the sequence of properties.
// <<2>> is the Primary string above.
#Principia_OrbitAnalyser_OrbitDescription = <<2>><<1>>轨道
// <<12>> is the primary, in three forms as above.
// <<3>> is the sequence of properties, ending with a space.
#Principia_OrbitAnalyser_OrbitDescription = <<1>><<3>>轨道
#Principia_OrbitAnalyser_Elements_LowestAltitude = 最低轨道高度
#Principia_OrbitAnalyser_Warning_Collision = 发生撞击
#Principia_OrbitAnalyser_Warning_CollisionRisk = 可能发生撞击
@@ -239,13 +248,11 @@ Localization {
// MapNodePool
#Principia_MapNode_Planned = 计划的
#Principia_MapNode_Predicted = 预测的
// For the apsides, <<1>> is the name of the celestial.
#Principia_MapNode_Periapsis = <<1>>近拱点
#Principia_MapNode_Apoapsis = <<1>>远拱点
// <<1>> is the source (predicted or planned).
// <<2>> is the apsis (from the strings above).
// <<3>> is the altitude in metres.
#Principia_MapNode_ApsisHeader = <<1>><<2>>:\n<<3>> m
// For the apsides, <<12>> is the name of the relevant celestial (in two forms as above).
// <<3>> is the source (predicted or planned).
// <<4>> is the altitude in metres.
#Principia_MapNode_PeriapsisHeader = <<3>><<1>>近拱点:\n<<4>> m
#Principia_MapNode_ApoapsisHeader = <<3>><<1>>远拱点:\n<<4>> m
#Principia_MapNode_ApsisCaptionLine2 = <<1>> m/s // <<1>>: speed.FormatN(0).
#Principia_MapNode_AscendingNode = 升交点
#Principia_MapNode_DescendingNode = 降交点
86 changes: 65 additions & 21 deletions ksp_plugin_adapter/localization_extensions.cs
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ namespace ksp_plugin_adapter {

internal static class L10N {
private const string english_us_ = "en-us";
private const string french_ = "fr-fr";

public static bool IsCJKV(string text) {
return Regex.IsMatch(
@@ -28,23 +29,60 @@ public static string Standalone(string name) {
return Localizer.Format("#Principia_GrammaticalForm_Standalone", name);
}

public static string NameWithoutArticle(this CelestialBody body) {
// This will need to be adjusted when we add support for other languages
// with articles.
return (Localizer.CurrentLanguage == english_us_ &&
body.displayName.StartsWith("The ")) ? body.name : body.displayName;
public static string StandaloneName(this CelestialBody celestial) {
return Standalone(celestial.Name());
}

public static string NameWithArticle(this CelestialBody body) {
// TODO(egg): should we apped ^d instead of prepending "the"? Certainly
// we would need to do that for other languages.
return (Localizer.CurrentLanguage == english_us_ &&
body.displayName.StartsWith("The ")) ? "the " + body.name
: body.displayName;
private static bool StartsWithCapitalizedDefiniteArticle(string s) {
return (Localizer.CurrentLanguage == english_us_ && s.StartsWith("The ")) ||
(Localizer.CurrentLanguage == french_ &&
(s.StartsWith("La ") || s.StartsWith("Le ")));
}

public static string Initial(this CelestialBody body) {
return body.NameWithoutArticle()[0].ToString();
private static string LingoonaUnqualified(string s) {
return s.Split(new[]{'^'}, 2)[0];
}

private static string LingoonaQualifiers(string s) {
if (!s.Contains('^')) {
return "";
}
return s.Split(new[]{'^'}, 2)[1];
}

private static string LingoonaQualify(string s, string qualifiers) {
return qualifiers == "" ? s : $"{s}^{qualifiers}";
}

// Returns the localized name with the appropriate Lingoona grammatical tags.
// Note that for bodies that may have an article (the Moon, la Terre), the
// article is absent, and instead the gender is indicated in lowercase
// (Moon^n, Terre^f), so that an article may be requested using a placeholder
// of the form <<A:1>>.
// Bodies that may not have an article (Saturn, Vénus) have an uppercase
// gender tag (Saturn^N, Vénus^F).
public static string Name(this CelestialBody body) {
string name = LingoonaUnqualified(body.displayName);
string qualifiers = LingoonaQualifiers(body.displayName);
if (qualifiers == "") {
// Stock English has everything as a neuter name (^N).
// For mods that did not try tagging grammar (which is hardly a problem
// in English since stock strings do not add articles), tag as neuter name
// (and switch to neuter noun below if we see an article).
qualifiers = "N";
}
if (StartsWithCapitalizedDefiniteArticle(body.displayName)) {
name = name.Split(new[]{' '}, 2)[1];
// Lowercase the gender, allowing for articles.
qualifiers = char.ToLower(qualifiers[0]) + qualifiers.Substring(1);
}
return LingoonaQualify(name, qualifiers);
}

private static string Initial(this CelestialBody body) {
return IsCJKV(LingoonaUnqualified(body.displayName))
? body.displayName
: body.Name()[0].ToString();
}

public static string FormatOrNull(string template, params object[] args) {
@@ -66,18 +104,24 @@ private static string CelestialOverride(string template,
}

public static string CelestialString(string template,
Func<CelestialBody, string> name,
params CelestialBody[] args) {
string[] names = (from body in args select name(body)).ToArray();
return CelestialOverride(template, names, args) ??
CelestialBody[] bodies,
params object[] args) {
string[] names =
(from body in bodies
from name in new[]{body.Name(), body.Initial()}
select name).Concat(from arg in args select arg.ToString()).ToArray();
return CelestialOverride(template, names, bodies) ??
Localizer.Format(template, names);
}

public static string CelestialStringOrNull(string template,
Func<CelestialBody, string> name,
params CelestialBody[] args) {
string[] names = (from body in args select name(body)).ToArray();
return CelestialOverride(template, names, args) ??
CelestialBody[] bodies,
params object[] args) {
string[] names =
(from body in bodies
from name in new[]{body.Name(), body.Initial()}
select name).Concat(from arg in args select arg.ToString()).ToArray();
return CelestialOverride(template, names, bodies) ??
FormatOrNull(template, names);
}
}
20 changes: 8 additions & 12 deletions ksp_plugin_adapter/map_node_pool.cs
Original file line number Diff line number Diff line change
@@ -209,19 +209,15 @@ private KSP.UI.Screens.Mapview.MapNode MakePoolNode() {
case MapObject.ObjectType.Periapsis:
case MapObject.ObjectType.Apoapsis: {
CelestialBody celestial = properties.reference_frame.Centre();
string apsis_name = L10N.CelestialString(
properties.object_type == MapObject.ObjectType.Periapsis
? "#Principia_MapNode_Periapsis"
: "#Principia_MapNode_Apoapsis",
L10N.NameWithoutArticle,
celestial);
Vector3d position = properties.world_position;
double speed = properties.velocity.magnitude;
caption.Header = Localizer.Format("#Principia_MapNode_ApsisHeader",
source,
apsis_name,
celestial.GetAltitude(position).
FormatN(0));
caption.Header = L10N.CelestialString(
properties.object_type == MapObject.ObjectType.Periapsis
? "#Principia_MapNode_PeriapsisHeader"
: "#Principia_MapNode_ApoapsisHeader",
new[]{celestial},
source,
celestial.GetAltitude(position).FormatN(0));
caption.captionLine2 =
Localizer.Format("#Principia_MapNode_ApsisCaptionLine2",
speed.FormatN(0));
@@ -260,7 +256,7 @@ private KSP.UI.Screens.Mapview.MapNode MakePoolNode() {
CelestialBody celestial = properties.reference_frame.Centre();
caption.Header = Localizer.Format("#Principia_MapNode_ImpactHeader",
source,
celestial.name);
celestial.Name());
caption.captionLine1 = "";
caption.captionLine2 = "";
break;
55 changes: 22 additions & 33 deletions ksp_plugin_adapter/orbit_analyser.cs
Original file line number Diff line number Diff line change
@@ -278,7 +278,7 @@ protected override void RenderWindow(int window_id) {
} else {
duration_in_revolutions = Localizer.Format(
"#Principia_OrbitAnalyser_Warning_NoElements2",
primary.NameWithArticle());
primary.Name());
}
multiline_style = Style.Warning(multiline_style);
}
@@ -291,7 +291,7 @@ protected override void RenderWindow(int window_id) {
: Localizer.Format(
"#Principia_OrbitAnalyser_AnalysisDescription",
predicted_vessel.vesselName,
primary.NameWithArticle(),
primary.Name(),
mission_duration.FormatDuration(show_seconds: false),
duration_in_revolutions);

@@ -322,10 +322,6 @@ public static string OrbitDescription(CelestialBody primary,
if (!elements.HasValue) {
return null;
}
var primary_string = L10N.CelestialString(
"#Principia_OrbitAnalyser_OrbitDescription_Primary",
L10N.NameWithoutArticle,
primary);
string properties = "";
bool circular = false;
bool equatorial = false;
@@ -374,29 +370,24 @@ public static string OrbitDescription(CelestialBody primary,
switch (recurrence.Value.nuo) {
case 1:
if (circular && equatorial) {
var stationary_primary_string = L10N.CelestialStringOrNull(
"#Principia_OrbitAnalyser_OrbitDescription_StationaryPrimary",
L10N.NameWithoutArticle,
primary);
if (stationary_primary_string == null) {
properties = Localizer.Format(
"#Principia_OrbitAnalyser_OrbitDescription_Stationary");
} else {
primary_string = stationary_primary_string;
properties = "";
var stationary_string = L10N.CelestialStringOrNull(
"#Principia_OrbitAnalyser_OrbitDescription_Stationary",
new[]{primary});
if (stationary_string != null) {
return stationary_string;
}
properties = Localizer.Format(
"#Principia_OrbitAnalyser_OrbitDescription_Stationary");
} else {
var synchronous_primary_string = L10N.CelestialStringOrNull(
"#Principia_OrbitAnalyser_OrbitDescription_SynchronousPrimary",
L10N.NameWithoutArticle,
primary);
if (synchronous_primary_string == null) {
properties +=
Localizer.Format(
"#Principia_OrbitAnalyser_OrbitDescription_Synchronous");
} else {
primary_string = synchronous_primary_string;
var synchronous_string = L10N.CelestialStringOrNull(
"#Principia_OrbitAnalyser_OrbitDescription_Synchronous",
new[]{primary},
properties);
if (synchronous_string != null) {
return synchronous_string;
}
properties += Localizer.Format(
"#Principia_OrbitAnalyser_OrbitDescription_Synchronous");
}
break;
case 2:
@@ -409,9 +400,9 @@ public static string OrbitDescription(CelestialBody primary,
}
}
}
return Localizer.Format("#Principia_OrbitAnalyser_OrbitDescription",
properties,
primary_string);
return L10N.CelestialString("#Principia_OrbitAnalyser_OrbitDescription",
new[]{primary},
properties);
}

private void RenderLowestAltitude(OrbitalElements? elements,
@@ -467,12 +458,10 @@ private void RenderOrbitalElements(OrbitalElements? elements,
elements?.nodal_precession.FormatAngularFrequency());
string periapsis = L10N.CelestialString(
"#Principia_OrbitAnalyser_Elements_Periapsis",
L10N.NameWithArticle,
primary);
new[]{primary});
string apoapsis = L10N.CelestialString(
"#Principia_OrbitAnalyser_Elements_Apoapsis",
L10N.NameWithArticle,
primary);
new[]{primary});
LabeledField(
Localizer.Format(
"#Principia_OrbitAnalyser_Elements_ArgumentOfPeriapsis",
93 changes: 55 additions & 38 deletions ksp_plugin_adapter/reference_frame_selector.cs
Original file line number Diff line number Diff line change
@@ -126,8 +126,9 @@ public void SetToSurfaceFrameOf(CelestialBody celestial) {
}

private static string TargetFrameName(Vessel target) {
return Localizer.Format("#Principia_ReferenceFrameSelector_Name_Target",
target.orbit.referenceBody.NameWithoutArticle());
return L10N.CelestialString(
"#Principia_ReferenceFrameSelector_Name_Target",
new []{target.orbit.referenceBody});
}

private static string Name(FrameType type,
@@ -136,8 +137,7 @@ private static string Name(FrameType type,
case FrameType.BODY_CENTRED_NON_ROTATING:
return L10N.CelestialString(
"#Principia_ReferenceFrameSelector_Name_BodyCentredNonRotating",
L10N.NameWithoutArticle,
selected);
new[]{selected});
case FrameType.BARYCENTRIC_ROTATING:
if (selected.is_root()) {
throw Log.Fatal("Naming barycentric rotating frame of root body");
@@ -151,14 +151,12 @@ private static string Name(FrameType type,
} else {
return L10N.ZWSPToHyphenBetweenNonCJK(L10N.CelestialString(
"#Principia_ReferenceFrameSelector_Name_BodyCentredParentDirection",
L10N.NameWithoutArticle,
selected, selected.referenceBody));
new[]{selected, selected.referenceBody}));
}
case FrameType.BODY_SURFACE:
return L10N.CelestialString(
"#Principia_ReferenceFrameSelector_Name_BodySurface",
L10N.NameWithoutArticle,
selected);
new[]{selected});
default:
throw Log.Fatal("Unexpected type " + type.ToString());
}
@@ -191,8 +189,7 @@ private static string Abbreviation(FrameType type, CelestialBody selected) {
case FrameType.BODY_CENTRED_NON_ROTATING:
return L10N.CelestialStringOrNull(
"#Principia_ReferenceFrameSelector_Abbreviation_BodyCentredNonRotating",
L10N.Initial,
selected);
new[]{selected});
case FrameType.BARYCENTRIC_ROTATING:
return "DEPRECATED";
case FrameType.BODY_CENTRED_PARENT_DIRECTION:
@@ -202,30 +199,54 @@ private static string Abbreviation(FrameType type, CelestialBody selected) {
} else {
return L10N.CelestialStringOrNull(
"#Principia_ReferenceFrameSelector_Abbreviation_BodyCentredParentDirection",
L10N.Initial,
selected, selected.referenceBody);
new[]{selected, selected.referenceBody});
}
case FrameType.BODY_SURFACE:
return L10N.CelestialStringOrNull(
"#Principia_ReferenceFrameSelector_Abbreviation_BodySurface",
L10N.Initial,
selected);
new[]{selected});
default:
throw Log.Fatal("Unexpected type " + type.ToString());
}
}

private static string NavballName(FrameType type,
CelestialBody selected) {
// TODO(egg): I am not sure how this should generalize, so I am
// special-casing it here. Revisit when we have more languages.
if (Localizer.CurrentLanguage == "zh-cn" &&
type == FrameType.BODY_CENTRED_PARENT_DIRECTION &&
!L10N.IsCJKV(L10N.Standalone(selected.NameWithoutArticle())) &&
!L10N.IsCJKV(L10N.Standalone(selected.referenceBody.NameWithoutArticle()))) {
return $"{selected.name[0]}{selected.referenceBody.name[0]}轨道";
string result = Abbreviation(type, selected);
if (result != null) {
return result;
}
return Abbreviation(type, selected) ?? Name(type, selected);
switch (type) {
case FrameType.BODY_CENTRED_NON_ROTATING:
result = L10N.CelestialStringOrNull(
"#Principia_ReferenceFrameSelector_NavballName_BodyCentredNonRotating",
new[]{selected});
break;
case FrameType.BARYCENTRIC_ROTATING:
result = "DEPRECATED";
break;
case FrameType.BODY_CENTRED_PARENT_DIRECTION:
if (selected.is_root()) {
throw Log.Fatal(
"Naming parent-direction rotating frame of root body");
} else {
result = L10N.CelestialStringOrNull(
"#Principia_ReferenceFrameSelector_NavballName_BodyCentredParentDirection",
new[]{selected, selected.referenceBody});
}
break;
case FrameType.BODY_SURFACE:
result = L10N.CelestialStringOrNull(
"#Principia_ReferenceFrameSelector_NavballName_BodySurface",
new[]{selected});
break;
default:
throw Log.Fatal("Unexpected type " + type.ToString());
}
if (result != null) {
return result;
}
return Name(type, selected);
}

private static string SelectorText(FrameType type,
@@ -259,19 +280,19 @@ private static string SelectorTooltip(FrameType type,
return Localizer.Format(
"#Principia_ReferenceFrameSelector_Tooltip_BodyCentredNonRotating",
name,
selected.NameWithArticle());
selected.Name());
case FrameType.BARYCENTRIC_ROTATING:
return "DEPRECATED";
case FrameType.BODY_CENTRED_PARENT_DIRECTION:
return Localizer.Format(
"#Principia_ReferenceFrameSelector_Tooltip_BodyCentredParentDirection",
name,
selected.NameWithArticle());
selected.Name());
case FrameType.BODY_SURFACE:
return Localizer.Format(
"#Principia_ReferenceFrameSelector_Tooltip_BodySurface",
name,
selected.NameWithArticle());
selected.Name());
default:
throw Log.Fatal("Unexpected type " + type.ToString());
}
@@ -282,7 +303,7 @@ private static string TargetFrameDescription(Vessel target) {
return Localizer.Format(
"#Principia_ReferenceFrameSelector_Description_Target",
target.vesselName,
target.orbit.referenceBody.NameWithArticle());
target.orbit.referenceBody.Name());
}

private static string Description(FrameType type,
@@ -291,8 +312,7 @@ private static string Description(FrameType type,
case FrameType.BODY_CENTRED_NON_ROTATING:
return L10N.CelestialString(
"#Principia_ReferenceFrameSelector_Description_BodyCentredNonRotating",
L10N.NameWithArticle,
selected);
new[]{selected});
case FrameType.BARYCENTRIC_ROTATING:
return "DEPRECATED";
case FrameType.BODY_CENTRED_PARENT_DIRECTION:
@@ -302,14 +322,12 @@ private static string Description(FrameType type,
} else {
return L10N.CelestialString(
"#Principia_ReferenceFrameSelector_Description_BodyCentredParentDirection",
L10N.NameWithArticle,
selected, selected.referenceBody);
new[]{selected, selected.referenceBody});
}
case FrameType.BODY_SURFACE:
return L10N.CelestialString(
"#Principia_ReferenceFrameSelector_Description_BodySurface",
L10N.NameWithArticle,
selected);
new[]{selected});
default:
throw Log.Fatal("Unexpected type " + type.ToString());
}
@@ -336,16 +354,16 @@ public string ReferencePlaneDescription() {
frame_type == FrameType.BODY_SURFACE)) {
return Localizer.Format(
"#Principia_ReferenceFrameSelector_ReferencePlane_Centred",
selected_celestial.NameWithArticle());
selected_celestial.Name());
}
string secondary =
target_frame_selected
? Localizer.Format(
"#Principia_ReferenceFrameSelector_ReferencePlane_Secondary_Target")
: selected_celestial.NameWithArticle();
: selected_celestial.Name();
string primary = target_frame_selected
? selected_celestial.NameWithArticle()
: selected_celestial.referenceBody.NameWithArticle();
? selected_celestial.Name()
: selected_celestial.referenceBody.Name();
return Localizer.Format("#Principia_ReferenceFrameSelector_ReferencePlane",
secondary,
primary);
@@ -498,8 +516,7 @@ private void RenderSubtree(CelestialBody celestial, int depth) {
expanded_[celestial] = !expanded_[celestial];
}
}
UnityEngine.GUILayout.Label(
L10N.Standalone(celestial.NameWithoutArticle()));
UnityEngine.GUILayout.Label(celestial.StandaloneName());
UnityEngine.GUILayout.FlexibleSpace();
if (celestial.is_root()) {
UnityEngine.GUILayout.Label(