Skip to content

Commit 52d1313

Browse files
committed
add translation API methods to get translation keys
1 parent 6c9c20c commit 52d1313

5 files changed

Lines changed: 107 additions & 23 deletions

File tree

docs/release-notes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
* For mod authors:
1212
* Mod events are now raised on the shipping menu (except when it's actually saving).
13+
* Added translation API methods to query translation keys (`ContainsKey` and `GetKeys`).
1314
* Fixed the game's `Data/ChairTiles` logic not handling unique string IDs like `Maps/Author.ModName` correctly.
1415
* Fixed exception thrown if `modRegistry.GetApi<T>` can't proxy the API to the given interface. It now logs an error and returns null as intended.
1516

src/SMAPI.Tests/Core/TranslationTests.cs

Lines changed: 74 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,18 @@ public class TranslationTests
2424
/// <summary>Sample translation text for unit tests.</summary>
2525
public static string?[] Samples = [null, "", " ", "boop", " boop "];
2626

27+
/// <summary>The locale to check for unit tests.</summary>
28+
public const string TestLocale = "en";
29+
30+
/// <summary>A translation key which exists in both <c>default.json</c> and <c>en.json</c>.</summary>
31+
public const string DefaultAndLocalKey = "key A";
32+
33+
/// <summary>A translation key which only exists in <c>en.json</c>.</summary>
34+
public const string LocalKey = "key B";
35+
36+
/// <summary>A translation key which only exists in <c>default.json</c>.</summary>
37+
public const string DefaultKey = "key C";
38+
2739

2840
/*********
2941
** Unit tests
@@ -36,35 +48,62 @@ public void Helper_HandlesNoTranslations()
3648
{
3749
// arrange
3850
var data = new Dictionary<string, IDictionary<string, string>>();
51+
ITranslationHelper helper = this.GetSampleHelper(data);
3952

4053
// act
41-
ITranslationHelper helper = new TranslationHelper(this.CreateModMetadata(), "en", LocalizedContentManager.LanguageCode.en).SetTranslations(data);
4254
Translation translation = helper.Get("key");
43-
Translation[]? translationList = helper.GetTranslations()?.ToArray();
55+
Translation[] translationList = helper.GetTranslations().ToArray();
4456

4557
// assert
46-
helper.Locale.Should().Be("en");
58+
helper.Locale.Should().Be(TranslationTests.TestLocale);
4759
helper.LocaleEnum.Should().Be(LocalizedContentManager.LanguageCode.en);
4860
translationList.Should().NotBeNull().And.BeEmpty();
4961

5062
translation.Should().NotBeNull();
5163
translation.ToString().Should().Be(this.GetPlaceholderText("key"));
5264
}
5365

66+
[Test(Description = $"Assert that {nameof(ITranslationHelper.ContainsKey)} returns the expected value.")]
67+
[TestCase(TranslationTests.DefaultAndLocalKey, ExpectedResult = true)]
68+
[TestCase(TranslationTests.LocalKey, ExpectedResult = true)]
69+
[TestCase(TranslationTests.DefaultKey, ExpectedResult = true)]
70+
[TestCase("missing-key", ExpectedResult = false)]
71+
public bool Helper_ContainsKey(string key)
72+
{
73+
// arrange
74+
ITranslationHelper helper = this.GetSampleHelper();
75+
76+
// act & assert
77+
return helper.ContainsKey(key);
78+
}
79+
80+
[Test(Description = $"Assert that {nameof(ITranslationHelper.GetKeys)} returns the expected keys.")]
81+
public void Helper_GetKeys()
82+
{
83+
// arrange
84+
string[] expectedKeys = [TranslationTests.DefaultKey, TranslationTests.DefaultAndLocalKey, TranslationTests.LocalKey];
85+
ITranslationHelper helper = this.GetSampleHelper();
86+
87+
// act
88+
IEnumerable<string> actualKeys = helper.GetKeys();
89+
90+
// assert
91+
actualKeys.Should().BeEquivalentTo(expectedKeys);
92+
}
93+
5494
[Test(Description = "Assert that the translation helper returns the expected translations correctly.")]
5595
public void Helper_GetTranslations_ReturnsExpectedText()
5696
{
5797
// arrange
58-
var data = this.GetSampleData();
5998
var expected = this.GetExpectedTranslations();
99+
TranslationHelper helper = this.GetSampleHelper();
60100

61101
// act
62102
var actual = new Dictionary<string, Translation[]?>();
63-
TranslationHelper helper = new TranslationHelper(this.CreateModMetadata(), "en", LocalizedContentManager.LanguageCode.en).SetTranslations(data);
64103
foreach (string locale in expected.Keys)
65104
{
66105
this.AssertSetLocale(helper, locale, LocalizedContentManager.LanguageCode.en);
67-
actual[locale] = helper.GetTranslations()?.ToArray();
106+
actual[locale] = helper.GetTranslations().ToArray();
68107
}
69108

70109
// assert
@@ -80,12 +119,11 @@ public void Helper_GetTranslations_ReturnsExpectedText()
80119
public void Helper_Get_ReturnsExpectedText()
81120
{
82121
// arrange
83-
var data = this.GetSampleData();
84122
var expected = this.GetExpectedTranslations();
123+
TranslationHelper helper = this.GetSampleHelper();
85124

86125
// act
87126
var actual = new Dictionary<string, Translation[]>();
88-
TranslationHelper helper = new TranslationHelper(this.CreateModMetadata(), "en", LocalizedContentManager.LanguageCode.en).SetTranslations(data);
89127
foreach (string locale in expected.Keys)
90128
{
91129
this.AssertSetLocale(helper, locale, LocalizedContentManager.LanguageCode.en);
@@ -317,18 +355,18 @@ private IDictionary<string, IDictionary<string, string>> GetSampleData()
317355
{
318356
["default"] = new Dictionary<string, string>
319357
{
320-
["key A"] = "default A",
321-
["key C"] = "default C"
358+
[TranslationTests.DefaultAndLocalKey] = "default A",
359+
[TranslationTests.DefaultKey] = "default C"
322360
},
323-
["en"] = new Dictionary<string, string>
361+
[TranslationTests.TestLocale] = new Dictionary<string, string>
324362
{
325-
["key A"] = "en A",
326-
["key B"] = "en B"
363+
[TranslationTests.DefaultAndLocalKey] = "en A",
364+
[TranslationTests.LocalKey] = "en B"
327365
},
328366
["en-US"] = new Dictionary<string, string>(),
329367
["zzz"] = new Dictionary<string, string>
330368
{
331-
["key A"] = "zzz A"
369+
[TranslationTests.DefaultAndLocalKey] = "zzz A"
332370
}
333371
};
334372
}
@@ -340,25 +378,38 @@ private IDictionary<string, Translation[]> GetExpectedTranslations()
340378
{
341379
["default"] =
342380
[
343-
new Translation("default", "key A", "default A"),
344-
new Translation("default", "key C", "default C")
381+
new Translation("default", TranslationTests.DefaultAndLocalKey, "default A"),
382+
new Translation("default", TranslationTests.DefaultKey, "default C")
345383
],
346-
["en"] =
384+
[TranslationTests.TestLocale] =
347385
[
348-
new Translation("en", "key A", "en A"),
349-
new Translation("en", "key B", "en B"),
350-
new Translation("en", "key C", "default C")
386+
new Translation(TranslationTests.TestLocale, TranslationTests.DefaultAndLocalKey, "en A"),
387+
new Translation(TranslationTests.TestLocale, TranslationTests.LocalKey, "en B"),
388+
new Translation(TranslationTests.TestLocale, TranslationTests.DefaultKey, "default C")
351389
],
352390
["zzz"] =
353391
[
354-
new Translation("zzz", "key A", "zzz A"),
355-
new Translation("zzz", "key C", "default C")
392+
new Translation("zzz", TranslationTests.DefaultAndLocalKey, "zzz A"),
393+
new Translation("zzz", TranslationTests.DefaultKey, "default C")
356394
]
357395
};
358-
expected["en-us"] = expected["en"].ToArray();
396+
expected["en-us"] = expected[TranslationTests.TestLocale].ToArray();
359397
return expected;
360398
}
361399

400+
/// <summary>Get a translation helper with sample data matching <see cref="GetSampleData"/>.</summary>
401+
private TranslationHelper GetSampleHelper()
402+
{
403+
return this.GetSampleHelper(this.GetSampleData());
404+
}
405+
406+
/// <summary>Get a translation helper with sample data matching <see cref="GetSampleData"/>.</summary>
407+
/// <param name="data">The translation data to use.</param>
408+
private TranslationHelper GetSampleHelper(IDictionary<string, IDictionary<string, string>> data)
409+
{
410+
return new TranslationHelper(this.CreateModMetadata(), TranslationTests.TestLocale, LocalizedContentManager.LanguageCode.en).SetTranslations(data);
411+
}
412+
362413
/// <summary>Get the default placeholder text when a translation is missing.</summary>
363414
/// <param name="key">The translation key.</param>
364415
private string GetPlaceholderText(string key)

src/SMAPI/Framework/ModHelpers/TranslationHelper.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,18 @@ public TranslationHelper(IModMetadata mod, string locale, LocalizedContentManage
3737
this.Translator.SetLocale(locale, languageCode);
3838
}
3939

40+
/// <inheritdoc />
41+
public bool ContainsKey(string key)
42+
{
43+
return this.Translator.ContainsKey(key);
44+
}
45+
46+
/// <inheritdoc />
47+
public IEnumerable<string> GetKeys()
48+
{
49+
return this.Translator.GetKeys();
50+
}
51+
4052
/// <inheritdoc />
4153
public IEnumerable<Translation> GetTranslations()
4254
{

src/SMAPI/Framework/Translator.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,19 @@ public void SetLocale(string locale, LocalizedContentManager.LanguageCode locale
5656
}
5757
}
5858

59+
/// <summary>Get whether a translation key exists for the current locale (including its fallback locales).</summary>
60+
/// <param name="key">The translation key.</param>
61+
public bool ContainsKey(string key)
62+
{
63+
return this.ForLocale.ContainsKey(key);
64+
}
65+
66+
/// <summary>Get the translation keys which exist for the current locale (including its fallback locales).</summary>
67+
public IEnumerable<string> GetKeys()
68+
{
69+
return this.ForLocale.Keys;
70+
}
71+
5972
/// <summary>Get all translations for the current locale.</summary>
6073
public IEnumerable<Translation> GetTranslations()
6174
{

src/SMAPI/ITranslationHelper.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ public interface ITranslationHelper : IModLinked
1919
/*********
2020
** Public methods
2121
*********/
22+
/// <summary>Get whether a translation key exists for the current locale (including its fallback locales).</summary>
23+
/// <param name="key">The translation key.</param>
24+
bool ContainsKey(string key);
25+
26+
/// <summary>Get the translation keys which exist for the current locale (including its fallback locales).</summary>
27+
IEnumerable<string> GetKeys();
28+
2229
/// <summary>Get all translations for the current locale.</summary>
2330
IEnumerable<Translation> GetTranslations();
2431

0 commit comments

Comments
 (0)