NSLocalizedString
family. We have created a set of similar methods BPFLocalizedString
(the BPF prefix means Badoo Platform Foundation) and use them throughout the application. "Under the hood" BPFLocalizedString
uses a localization service that contains all the data and implements the basic functionality. We save all updates from the server in a separate bundle. When the client code requests a localized string, we look for the necessary message in this bundle and, if necessary, return to the default localization bundle. public func localizedStringForKey(_ key: String) - > String { let str = self.localizationsBundle.localizedString(forKey: key) return str == key ? Bundle.main.localizedString(forKey: key, value: nil, table: nil) : str }
BPFLocalizedString
API for BPFLocalizedString
looks like this: NSString * __nonnull BPFLocalizedString(NSString * __nonnull key, NSString * __nullable comment); public func BPFLocalizedString(_ key: String) - > String { return BPFGlobals.shared().localizedStringsService.localizedStringForKey( key) }
NSLocalizedString
with the corresponding BPFLocalizedString
. Fortunately, this task can be easily solved using automatic scripts. Another limitation is the impossibility of applying BPFLocalizedString
directly to statically packaged user interface elements (XIB and Storyboard). This is a completely natural limitation, since we replace the static localization with the dynamic one.Resources
is part of the Context
interface. Resources
provides the configuration of the current device (location, screen size, orientation, and so on). One solution is to replace all Resources.getString()
with our own custom implementation, as in iOS. But we chose a more elegant way.Resources
instead of the system one? Fortunately, this is possible! Take the Activity
class, write its successor, and apply it everywhere: public abstract class BaseActivity extends Activity { private Resources mResources; public Resources getResources() { if (mResources == null) { Resources r = super.getResources(); mResources = new ResourceWrapper(this, r); } return mResources; } }
Resources
to retrieve the updated values ​​of the tokens: public class ResourceWrapper extends Resources { private final Resources mResources; private final LexemeProvider mLexemeProvider; public ResourceWrapper(Context context, Resources r) { super(r.getAssets(), r.getDisplayMetrics(), r.getConfiguration()); mResources = resources; mLexemeProvider = new LexemeProvider(...); } @Override public String getString(@StringRes int id) throws NotFoundException{ String hotString = mLexemeProvider.getString(id); if (hotString == null) { return mResources.getString(id); } else { return hotString; } // Override each method and return corresponding value from mResources @Override public boolean getBoolean(int id) throws NotFoundException { return mResources.getBoolean(id); } }
ResourceWrapper
to this particular implementation ( mResources
in our case).TextView
calls context.getTheme (). ObtainStyledAttributes (...) .getText (...) to get the corresponding values, in which case our replacement for Resources
no longer works.LocalizationProvider
here too. public class DynamicLexemeInflater { private static void applyDynamicLexems(View view, String name, Context context, AttributeSet attrs) { if (view instanceof TextView) { TextView textView = (TextView) view; TypedArray typedArray = context.obtainStyledAttributes(attrs, new int[] { android.R.attr.text, android.R.attr.hint }); int textResourceId = typedArray.getResourceId(0, -1); if (textResourceId != -1) { String dispatchedString = context.getString(textResourceId); textView.setText(dispatchedString); } int hintResourceId = typedArray.getResourceId(1, -1); if (hintResourceId != -1) { String dispatchedString = context.getString(hintResourceId); textView.setHint(dispatchedString); } typedArray.recycle(); } }
InflaterFactory
to add “post-processing” “inflated” views in which text values ​​are specified.AppResources
class (or you can give it any other name), which contains static getters for all strings used in the application. Here is what is inside these getters: public static string ApplicationTitle { get { return ResourceManager.GetString("ApplicationTitle", resourceCulture); } }
ResourceManager
property is used here, defined as public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PhoneApp.Resources.AppResources", typeof (AppResources).Assembly); resourceMan = temp; } return resourceMan; } }
System.Resources.ResourceManager
is a central component of the localization API, it takes all the hard work of loading current string values. Fortunately, in the overloaded method it has an extension point:public virtual string GetString(string name, CultureInfo culture)
. That is what we need to implement our machinery and to supplement the means provided by the system. It is necessary only to inherit from this class and overload the GetString
method: public class UpdateableResourceManager: ResourceManager { public override string GetString(string name, CultureInfo culture) { var lexemesHandler = _localizationService.GetLexemesHandler(culture); return lexemesHandler?.GetLexeme(name)?.Value?.Text ?? base.GetString(name, culture); } }
UpdateableResourceManager
instead of what is used by default in the AppResources
class. But since this class is automatically generated, you also need to gain control over the generation in order to add your data to the resulting file. This is usually done every time you open the AppResources
file in Visual Studio, but you can do it manually (or automatically in the script) using the RESGen tool, as in this PowerShell example: $resgenPath = “C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6 Tools\ResGen.exe” & $resgenPath AppResources.resx to_delete.txt “/str:cs,Badoo.Is.Ponies.Namespace,AppResources,AppResources.Designer.cs” / publicclass Remove — Item “to_delete.txt”
System.Resources.ResourceManager
line in our Badoo.Next.Big.Thing.UpdateableResourceManager
. The rest is processed in a separate LocalizationService, which is responsible for all the work with the network, data storage and retrieval.Source: https://habr.com/ru/post/348350/
All Articles