This guide explains how to add translations to the Crossword application.
The application uses a dual-localization approach:
- Server-side: ASP.NET Core localization with
.resxresource files - Client-side: JSON translation files loaded dynamically
Resource files are in: src/server/Resources/
SharedResources.resx- Default/English translationsSharedResources.ru.resx- Russian translationsSharedResources.uk.resx- Ukrainian translationsSharedResources.cs- Marker class for resource location
-
Open the
.resxfile in Visual Studio (or any text editor) -
Add a new data entry:
<data name="YourKey" xml:space="preserve">
<value>Your translated text</value>
</data>- Use in C# code:
using Microsoft.Extensions.Localization;
public class YourController : ControllerBase
{
private readonly IStringLocalizer<SharedResources> _localizer;
public YourController(IStringLocalizer<SharedResources> localizer)
{
_localizer = localizer;
}
public IActionResult SomeAction()
{
var translatedString = _localizer["YourKey"];
return Ok(new { message = translatedString.Value });
}
}In SharedResources.resx:
<data name="WelcomeMessage" xml:space="preserve">
<value>Welcome to Crossword Puzzles!</value>
</data>In SharedResources.ru.resx:
<data name="WelcomeMessage" xml:space="preserve">
<value>Добро пожаловать в кроссворды!</value>
</data>In SharedResources.uk.resx:
<data name="WelcomeMessage" xml:space="preserve">
<value>Ласкаво просимо до кросвордів!</value>
</data>Translation files are in: src/client/wwwroot/locales/
en.json- English translationsru.json- Russian translationsuk.json- Ukrainian translations
- Add entries to JSON files:
In en.json:
{
"myNewKey": "My English text"
}In ru.json:
{
"myNewKey": "Мой русский текст"
}In uk.json:
{
"myNewKey": "Мій український текст"
}- Use in JavaScript:
// Get translation
const translatedText = localeManager.t('myNewKey', 'Fallback text');
// Use in HTML
document.getElementById('myElement').textContent = localeManager.t('myNewKey');To translate existing UI elements:
- Update HTML to use data attributes or ids:
<button id="checkBtn" data-i18n="checkAnswers">Check Answers</button>- Apply translations on page load:
document.addEventListener('DOMContentLoaded', async () => {
// Wait for translations to load
await localeManager.loadTranslations();
// Update all elements
document.querySelectorAll('[data-i18n]').forEach(el => {
const key = el.getAttribute('data-i18n');
el.textContent = localeManager.t(key);
});
});The application detects language from:
- User selection: Clicking flag buttons (🇬🇧 🇷🇺 🇺🇦)
- localStorage: Persists user preference
- Accept-Language header: Sent with all API requests
- User clicks a flag → locale saved to localStorage
- Page reloads
locale.jsloads → reads locale from localStorage- Client loads translations from
/locales/{code}.json - All API requests include
Accept-Language: en/ru/ukheader - Server uses header to return localized responses
To add a new language (e.g., French):
- Server-side: Create
SharedResources.fr.resx - Client-side: Create
locales/fr.json - Update locale.js:
this.supportedLocales = {
'English': { code: 'en', flag: '🇬🇧', name: 'English' },
'Russian': { code: 'ru', flag: '🇷🇺', name: 'Русский' },
'Ukrainian': { code: 'uk', flag: '🇺🇦', name: 'Українська' },
'French': { code: 'fr', flag: '🇫🇷', name: 'Français' }
};- Update Program.cs:
var supportedCultures = new[]
{
new CultureInfo("en"),
new CultureInfo("ru"),
new CultureInfo("uk"),
new CultureInfo("fr")
};- Use descriptive keys:
welcomeMessagenotmsg1 - Keep fallbacks: Always provide default text
- Test all languages: Verify translations display correctly
- Avoid hardcoded strings: Use translation keys everywhere
- Consider text length: Translations may be longer/shorter than English
Client-side translations include:
- Button labels (Check Answers, Reveal Solution, etc.)
- Messages (Congratulations, Error messages)
- UI elements (Loading, Clear, etc.)
You can add more translations to the JSON files as needed!