Резюме для программиста
1. Все строки хранятся в одном файле .resx для каждой локали. В файле несколько тысяч записей.
Обычно все так и делают.
В файле несколько тысяч записей.
Это нормально.
2. Чтобы во всей этой мути разбираться, ключ для каждой записи представляет собой строку, составляемую из параметров с разделителем - типа такого
{area}_{context}_{name}.
Нотация ключа не имеет значения.
3. Для параметров area и context заведены специальные классы с возможными значениями - типа context бывает "menu", "exception", "dialog" и т.д. - до двух десятков. То же и для area.
Норм. У нас так сделано. Очень удобно:
public class ResourseIds { public class Menu { public class File { public const string Exit = "Menu_File_Exit"; } } }
4. Чтобы вытащить нужную переведённую строку, программист конструирует строку-монстрика из 3-5 параметров и использует её как ключ.
Если это необходимо, но почему бы и нет?
5. Формат ключа {area}_{context}_{name} не постоянный и не обязательный. Некоторые строки имеют ключ просто из одного параметра, некоторые из 2 или 4. Сами параметры могут быть тоже замудрённые - типа area может быть составной "name1.name2-name3".
Да ради бога. Это же просто строка :) Главное, чтобы идентификатор был уникальным :)
6. Чтобы парсить всё это безумие, заведён специальный класс с кучей перегруженных функций, отрабатывающих все эти форматы и варианты составных ключей. На всё про всё около 1000 строк кода. Плюс несколько сотен строк для хранения возможных вариантов параметров в виде текстовых констант. Плюс в разных проектах и классах ещё несколько перегруженных функций для вытаскивания локализованной строки из главного класса по вытаскиванию - до 4-5 вложенных вызовов.
Не понятно только, зачем все это парсить? :)
Итого до 2 тысяч строк кода, пяток классов, до пяти, если не больше, вложенных вызовов функций. И это только чтобы просто вытащить одну локализованную строку. А уж оттестировать всё это - ухх, юнит-тесты будешь месяц делать.
Если у тебя есть пистолет и колено, то вегда можно выстрелить себе в колено :)
1. Все строки хранятся в одном файле .resx для каждой локали. В файле несколько тысяч записей.Обычно все так и делают.
А я думал, обычно хранят папку Resources и в ней повторяют структуру папок проекта, в которые кладут уже небольшие файлы типа Strings.en-US.resx, в которых составной ключ не требуется (т.к. небольшие). Или в папки проекта кладут папку Locals и в ней Strings.en-US.resx. Но не всё в одну кучу.
Всё в одну кучу и классы-монстры по парсингу этой кучи может и имело бы смысл, если бы на протяжении всего проекта придерживались этой идеологии - всё в одной куче. Но в парсящем классе заметил такие ветки - если не находим в общей куче глобальных ресурсов, то ищем в локальным кучках, не находим в локальных, ищем в ещё более локальных. Т.е. помимо общей кучи есть и маленькие кучки, в которых тоже надо порыться. В результате ни одного плюса ни одной парадигмы (всё в куче или всё локально) не получается, зато все минусы ото всех собраны.
Ну и многоформатный ключ, раздувающий парсинг на многие ветки if-else-switch. Причём какого-то хрена там на каждом шагу пишется лог. С учётом кучи вложенных вызовов и огромного дерева парсинга ключа, одно доставание одной локализованной строки может добавить до 10 записей в лог. Кто потом всю эту мешанину разбирает? Я тоже раньше думал, что чем подробнее залогируешь, тем лучше. Но потом попробовал читать свои логи - ну нафиг. Чтобы читать подробные логи, нужны спецтулзы по парсингу подробных логов - иначе
задолбаешься. Но проходит время, тулзы теряются-забываются-не обновляются, а гигабайтные логи остаются.
Если у тебя есть пистолет и колено, то вегда можно выстрелить себе в колено :)
Сначала вы соглашаетесь почти со всеми пунктами "как делать не надо", а потом "ну всегда же можно выстрелить себе в ногу". Типа есть способ не выстрелить, если делать как не надо.
Многоформатный сложносоставный ключ - это первое решение, к которому приходят неопытные и те, кому надо "на коленке по-быстрому, потом разберёмся". "Потом" обычно на всё забивается.
Сначала вы соглашаетесь почти со всеми пунктами "как делать не надо", а потом "ну всегда же можно выстрелить себе в ногу".
Не надо писать свой парсер :) Тем более, что не понятно зачем кому-то вообще понадобился парсер идентивикаторов строк :)
Человечество уже давно придумало ResourceManager. Таботает как часы, быстро и нажедно.
Хочешь свою поделку - нет проблем. Менеджер ресурсов - это всего лишь таблица с 2 колонками: уникальный ключ <--> данные (строка, иконка, что угодно). Таким образом твоя задача - при старте программы запихнуть все имеющиеся строки в эту табличку, а дальше просто берешь данные по ключу. Все предельно просто. При этом менеджеру ресурсов глубоко плевать на нотацию ключа. Нотация ключа введена только для того, чтобы программисту было удобно читать код.
Ты можешь объяснить, зачем кому-то понадобилось парсить идентификатор ресурса?
Не надо писать свой парсер :) Тем более, что не понятно зачем кому-то вообще понадобился парсер идентивикаторов строк :)
Человечество уже давно придумало ResourceManager. Таботает как часы, быстро и нажедно.
Так он и использован. Но там ключи - строки. И вот эти строки они и конструируют через многосоставность и сотни строк кода.
а дальше просто берешь данные по ключу. Все предельно просто. При этом менеджеру ресурсов глубоко плевать на нотацию ключа. Нотация ключа введена только для того, чтобы программисту было удобно читать код.
Ага, просто. Под 2 тыщи строк на обработку и составление такого ключа. Мне вот почему-то не просто потом всё это разгребать. Просто кажется, когда ты с этим каждый день дело имеешь и сам сделал. Тебе самому кажется, что идея-то гениальная - всё в одном месте, просто строку конструируешь и всё. Ещё и по разным проектам разнесён стек вызовов ресурса. В одном проекте сами ресурсы, в другом - какой-нибудь "глобальный менеджер" по этим ресурсам, и в каждом отдельном проекте-потребителе свой менеджер-обработчик. И вот кучка проектов перекидывает друг другу эту жалкую строчку, тесно связывая эти проекты и раздувая код до сотен и тысяч строк. В результате чтобы проследить, как и откуда таки берётся одна строка и в чём там проблема, я должен открыть 5-7 вкладок с кодом - по всему стеку вызовов.
Ты можешь объяснить, зачем кому-то понадобилось парсить идентификатор ресурса?
ХЗ - ещё в этих простынях не до конца разобрался. Но всякие сименсы же не будут овнокод писать, да? Наверное, я чего-то не понимаю и там гениальные архитектурные идеи заложены, ага. )) Только приложение чего-то нихрена не делает, кроме как пачку форм показывает, а ощущение, будто проектировали управление галактикой.
Я понимаю, что вы не видели проект. Я просто спросил, как относитесь к такой организации ресурсов. Я бы весь этот один большой файл со сложным неочевидным ключом разнёс на файлы меньше, которым и ключа такого ({area}_{context}_{name}) бы не требовалось, т.к. ресурсы уже будут храниться в своих area и context. Соответственно и парсинг и составление ключа на сотни строк кода не нужен, стек вызовов короткий, отследить - куда проще, переделать или заменить - тоже. В моей же текущей каше проще сверху наворотить ещё один слой-обёртку, чем переписывать всё это дерьмо, включая все зависимости от этого глобального ресурса и его грёбаного формата ключа.
Впрочем, как я говорил, я подозреваю, что это "хитрый" ход команды будущих семизнаков по становлению незаменимыми и доению заказчика. )))
Но там ключи - строки.
Идентификатор ресурса в 99,99% случаев - строка.
И вот эти строки они и конструируют через многосоставность и сотни строк кода.
Должен быть какой-то разумный юз-кейс для этого :)
Но всякие сименсы же не будут овнокод писать, да? Наверное, я чего-то не понимаю, ага. ))
В сименсе тоже не боги код пишут :D Но для 2тыс строк кода должны быть требования :) Просто так их нисать не будут.
Но они делают это за высокую зарплату. )))
Нет :) Они работают по тарифу IGMetall. Так что можешь погуглить их зарплаты ;)
Короче, похрен на их зарплаты. Попытался сделать обёртку над этой системой {area}_{context}_{name}. Там они, оказывается, вызывают GetType() для area - ну т.е. часть составного ключа включает имят типа, для которого хранятся локализованные ресурсы. При попытке перенести это на Blazor, где у компонента обычно базовый тип должен называться Base (т.е. приписка Base к типу обязательна), получается, что я должен:
1 - называть свои типы так же, как у них - чтобы совпадало по area,
2 - вкорячить костыль отнимания Base от имени типа, опять же чтобы area совпало.
Гениальная архитектура - гибкость и удобства. Хрен перенесёшь на другую платформу или проект. К их тысячам костыльных строк кода для локализаций добавлю ещё свои несколько костылей.
Все строки хранятся в одном файле .resx для каждой локали. В файле несколько тысяч записей.
для десктопа вполне нормально.
ключ для каждой записи представляет собой строку, составляемую из параметров с разделителем
Если кто то любит мануальную трансляцию, так пусть делает подобную фигню. У нас все строки для десктопа вытаскивались на автомате и переводились первоначально также на автомате.
Чтобы вытащить нужную переведённую строку, программист конструирует строку-монстрика из 3-5 параметров и использует её как ключ.
не нужно ничего вытаскивать. Есть и другие варианты, например
И как оно работает? Типа каждому контролу на каждой форме свой айдишник присваивается - он и есть ключ? Ну и чтобы разработчик мог понять, какой айдишник какому контролу соответствует, то этот навороченный редактор ресурсов загружает каждую форму и показывает, на какой контрол какой айдишник закреплён?
Там они, оказывается, вызывают GetType() для area - ну т.е. часть составного ключа включает имят типа, для которого хранятся локализованные ресурсы. При попытке перенести это на Blazor, где у компонента обычно базовый тип должен называться Base (т.е. приписка Base к типу обязательна), получается, что я должен:
1 - называть свои типы так же, как у них - чтобы совпадало по area,
2 - вкорячить костыль отнимания Base от имени типа, опять же чтобы area совпало.
Нет, это я ступил. В Blazor все вызовы происходят из компонента, поэтому даже если он унаследован от базового типа, GetType() вернёт всё ранво тип компонента, а не базовый тип для этого компонента.
Т.е. если есть компонент MyComponent, и у него есть базовый тип с кодом (AlexNek должен понимать, о чём речь), называемый MyComponentBase, то даже если в MyComponentBase вызывается GetType(), то будучи использованным именно через компонент (т.е. через унаследованный тип MyComponent), он вернёт тип MyComponent, а не MyComponentBase.
Ладно, уже легче. Не нужно городить костыль.
Но архитектура их приложения всё равно оно... ))
Что за приколы?
Создаю проект библиотеки классов на .NET Framework 4.6.1. Там просто класс, который имеет один метод - принимает строку и возвращает свою строку, сконкатинированную с принмаемой.
Добавляю в солюшен консольное приложение на .NET 5. Оно просто выводит строку в консоль.
Добавляю в консольное приложение ссылку на проект библиотеки классов - добавилось.
Добавляю в консольном приложении using на пространство имён библиотеки классов из проекта на .NET Framework 4.6.1 - добавилось.
Использую в консольном приложении класс, возвращающий конкатинированную строку - т.е. посылаю ему свою строку и он возвращает её со своей добавкой.
Где несовместимость?!!!!!!!!!
Вот у человека тоже получилось. https://stackoverflow.com/questions/38148128/how-do-i-refe...
Вот ещё получилось https://docs.microsoft.com/en-us/answers/questions/336965/...
А везде говорят, что нельзя. Оно что, там внутри само сконвертнулось?
Попробовал в другом проекте, который переписываю - с кучей кода и зависимостей. Тоже получилось добавить и вызвать. Всё работает. Где несовместимость?
А, понятно - просто Коре проекты (к коим относятся и .NET 5+) компилируются и "только для Винды" (т.е. для платформы .NET Framework). Т.е. они после компиляции перестают быть кроссплатформенными. Я могу сослаться из Коре на .NET Framework, но не наоборот.
А нафига тогда Standard, если и само Коре и так хорошо работает с .NET Framework? Тут дядька чего-то рассказывает про Standard, но кейс .NETF -> .NET не рассматривает.
Зачем нужен .NET Standard, если что с ним, что без него все комбинации остаются одними и теми же?
cross platform:
.NET Standard
.NET
.NET + .NET Standard
Windows only:
.NET Framework
.NET Framework + .NET Standard
.NET Framework + .NET
.NET Framework + .NET Standard + .NET
Т.е. хочешь старый код на .NET Framework с любым новым - Windows only.
Хочешь cross platform - только новые фреймворки используй. Но если ты будешь использовать только новые .NET Standard и .NET, то проще сразу только .NET использовать.
Если это из-за времени - т.е. Standard появился ещё до .NET - то получается, на данный момент нет никакого смысла его использовать, т.к. для любой цели (cross platform, Windows only, смешанный код) он лишнее звено?
Ну и чтобы разработчик мог понять
Зачем ему что то понимать? По ресурсу находишь контрол и наоборот.
Всё задача разработчика сказать какие строки нужно локализировать.
Работает как парсер различного типа кода. Все найденные строки показывает в таблицах.
есть базовый тип с кодом (AlexNek должен понимать
Увы, есть только догадки Хотя как сделать базовый тип без кода догадок нет.