एक स्ट्रिंग में अन्य स्ट्रिंग से संदर्भ स्ट्रिंग। xml?


230

मैं अपनी स्ट्रिंग में एक स्ट्रिंग से एक संदर्भ संदर्भित करना चाहूंगा। xml फ़ाइल, नीचे की तरह (विशेष रूप से "message_text" स्ट्रिंग सामग्री के अंत पर ध्यान दें):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="button_text">Add item</string>
    <string name="message_text">You don't have any items yet! Add one by pressing the \'@string/button_text\' button.</string>
</resources>

मैंने उपरोक्त वाक्य रचना की कोशिश की है, लेकिन तब पाठ "@ string / button_text" को स्पष्ट पाठ के रूप में प्रिंट करता है। मुझे नहीं चाहिए। मैं संदेश पाठ को प्रिंट करना चाहूंगा "आपके पास अभी तक कोई आइटम नहीं है! 'आइटम जोड़ें' बटन दबाकर एक जोड़ें।"

क्या मुझे प्राप्त करने का कोई ज्ञात तरीका है?

राष्ट्रीय:
मेरे आवेदन में मदों की एक सूची है, लेकिन जब वह सूची खाली होती है तो मैं इसके बजाय एक "@android: id / खाली" दृश्य दिखाता हूं। उस TextView का पाठ उपयोगकर्ता को यह सूचित करना है कि नया आइटम कैसे जोड़ा जाए। मैं अपने लेआउट को बदलाव के लिए मूर्ख बनाना चाहता हूं (हां, मैं प्रश्न में मूर्ख हूं :-)


3
इसी तरह के एक और सवाल का जवाब मेरे लिए काम कर गया। कोई जावा आवश्यक नहीं है, लेकिन यह केवल एक ही संसाधन फ़ाइल के भीतर काम करता है।
21

जवाबों:


253

जावा कोड का उपयोग किए बिना xml में अक्सर उपयोग किए जाने वाले स्ट्रिंग (जैसे ऐप नाम) को डालने का एक अच्छा तरीका है: स्रोत

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE resources [
      <!ENTITY appname "MyAppName">
      <!ENTITY author "MrGreen">
    ]>

<resources>
    <string name="app_name">&appname;</string>
    <string name="description">The &appname; app was created by &author;</string>
</resources>

अपडेट करें:

आप अपनी इकाई को वैश्विक रूप से भी परिभाषित कर सकते हैं जैसे:

रेस / कच्चे / entities.ent:

<!ENTITY appname "MyAppName">
<!ENTITY author "MrGreen">

res / values ​​/ string.xml:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE resources [
    <!ENTITY % ents SYSTEM "./res/raw/entities.ent">
    %ents;   
]>

<resources>
    <string name="app_name">&appname;</string>
    <string name="description">The &appname; app was created by &author;</string>
</resources>

9
यह ओपी का सही उत्तर है। इस समाधान के अन्य महान लाभ हैं: (1) आप एक बाहरी फ़ाइल में डीटीडी को परिभाषित कर सकते हैं और अपने संसाधनों में हर जगह प्रतिस्थापन को लागू करने के लिए इसे किसी भी संसाधन फ़ाइल से संदर्भित कर सकते हैं। (2) एंड्रॉइड स्टूडियो आपको परिभाषित संस्थाओं का नाम बदलने / संदर्भ देने / रिफैक्ट करने देता है। हालांकि, यह लिखते समय स्वत: पूर्ण नाम नहीं है। (
Androidstudio

3
@RJFares आप 2 तरीके से जा सकते हैं: (a) "सहित" एक पूरी इकाई फ़ाइल EG: इस उत्तर को देखें । या (बी) : बाहरी डीटीडी में परिभाषित हर एक इकाई के रूप में यहाँ दिखाया गया है । ध्यान दें कि कोई भी पूर्ण नहीं है क्योंकि (ए) के साथ आप "रिफलेक्टिंग" क्षमताओं को ढीला कर देंगे और (बी) बहुत
क्रिया है

5
यदि आप इसे किसी Android लाइब्रेरी प्रोजेक्ट में उपयोग करते हैं तो सावधान - आप इसे अपने ऐप में अधिलेखित नहीं कर सकते।
ज़ायमीस

4
क्या ग्रैडल फ़ाइल में फ्लेवर को परिभाषित करते समय इकाई मूल्य को बदलना संभव है?
मियोच

7
एंड्रॉइड स्टूडियो द्वारा बाहरी संस्थाओं का समर्थन नहीं किया जाता है, क्योंकि बग की चर्चा यहां की जाती है: stackoverflow.com/a/51330953/8154765
Davide Cannizzo

181

जब तक आपके पूरे स्ट्रिंग में संदर्भ नाम होता है, तब तक एक दूसरे को संदर्भित करना संभव है। उदाहरण के लिए यह काम करेगा:

<string name="app_name">My App</string>
<string name="activity_title">@string/app_name</string>
<string name="message_title">@string/app_name</string>

डिफ़ॉल्ट मान सेट करने के लिए यह और भी उपयोगी है:

<string name="string1">String 1</string>
<string name="string2">String 2</string>
<string name="string3">String 3</string>
<string name="string_default">@string/string1</string>

अब आप string_defaultअपने कोड में हर जगह का उपयोग कर सकते हैं और किसी भी समय आसानी से डिफ़ॉल्ट बदल सकते हैं।


जो इस सवाल का जवाब भी देता है: मैं एक गतिविधि शीर्षक में app_name स्ट्रिंग का संदर्भ कैसे देता हूं। :)
स्टीफन होकिंग

53
यह "जब तक आप पूरे स्ट्रिंग को संदर्भित करते हैं तब तक नहीं पढ़ना चाहिए" (जिसे आप हमेशा परिभाषा के अनुसार कहते हैं) लेकिन "जब तक संदर्भित स्ट्रिंग संसाधन केवल संदर्भ नाम के होते हैं"।
sschuberth

54
यह वास्तव में एक संदर्भ नहीं है, यह सिर्फ एक उपनाम है, जो तार की रचना की समस्या को हल नहीं करता है।
एरिक वुड्रूफ़

2
इस सवाल का जवाब नहीं है, वे एक स्ट्रिंग दूसरे में एम्बेडेड चाहते थे।
intrepidis

1
FWIW, यह मेरे लिए बेहद उपयोगी था। धन्यवाद।
एलेन स्पार्टस

95

मुझे लगता है कि तुम नहीं कर सकते। लेकिन आप जैसा चाहें वैसा स्ट्रिंग "प्रारूपित" कर सकते हैं:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="button_text">Add item</string>
    <string name="message_text">You don't have any items yet! Add one by pressing the %1$s button.</string>
</resources>

कोड में:

Resources res = getResources();
String text = String.format(res.getString(R.string.message_text),
                            res.getString(R.string.button_text));

5
मैं वास्तव में "गैर-तर्क" समाधान की उम्मीद कर रहा था। फिर भी एक उचित पर्याप्त उत्तर (और इसकी सरासर गति आपको एक हरे रंग के चेक चिह्न को अनुदान देती है :-)
dbm

34
String text = res.getString(R.string.message_text, res.getString(R.string.button_text));थोड़ा साफ है।
एंड्री नोविकोव

34

Android में आप Xml के अंदर स्ट्रिंग्स को गाढ़ा नहीं कर सकते

निम्नलिखित समर्थित नहीं है

<string name="string_default">@string/string1 TEST</string>

इसे कैसे प्राप्त करें, जानने के लिए नीचे इस लिंक को देखें

एंड्रॉइड एक्सएमएल में कई तारों को कैसे मिलाएं?


16
खैर, मजेदार कहानी: यह एक ऐसे ही सवाल का जवाब है जिसका आप उल्लेख कर रहे हैं :-)
dbm

क्या इसको हासिल करने के लिए कोई रास्ता है?

यह @Francesco लॉरिता उत्तर का एक डुप्लिकेट है, कैसे प्रोग्राम को एक स्ट्रिंग को बदलने के लिए।
CoolMind

14

मैंने सरल सरल प्लगइन बनाया है जो आपको एक स्ट्रिंग को दूसरे से संदर्भित करने की अनुमति देता है। आप उन स्ट्रिंग्स का उल्लेख कर सकते हैं जो किसी अन्य फ़ाइल में परिभाषित हैं, उदाहरण के लिए विभिन्न बिल्ड वेरिएंट या लाइब्रेरी में। इस दृष्टिकोण के विपक्ष - आईडीई रिफ्लेक्टर को ऐसे संदर्भ नहीं मिलेंगे।

{{string_name}}स्ट्रिंग को संदर्भित करने के लिए सिंटैक्स का उपयोग करें :

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="super">Super</string>
    <string name="app_name">My {{super}} App</string>
    <string name="app_description">Name of my application is: {{app_name}}</string>
</resources>

प्लगइन को एकीकृत करने के लिए, बस अगला कोड आप ऐप या लाइब्रेरी मॉड्यूल स्तर build.gradleफ़ाइल में जोड़ें

buildscript {
  repositories {
    maven {
      url "https://plugins.gradle.org/m2/"
    }
  }
  dependencies {
    classpath "gradle.plugin.android-text-resolver:buildSrc:1.2.0"
  }
}

apply plugin: "com.icesmith.androidtextresolver"

अद्यतन: पुस्तकालय एंड्रॉइड ग्रेडल प्लगइन संस्करण 3.0 और इसके बाद के संस्करण के साथ काम नहीं करता है क्योंकि प्लगइन का नया संस्करण afl2 का उपयोग करता है। संसाधनों को .flat बाइनरी प्रारूप में पैक करता है, इसलिए पुस्तकालय के लिए पैक संसाधन अनुपलब्ध हैं। एक अस्थायी समाधान के रूप में आप अपनी gradle.properties फ़ाइल में android.enableAapt2 = false करके aapt2 को अक्षम कर सकते हैं।


मुझे यह विचार पसंद है .. लेकिन यह इस त्रुटि के साथ मेरे लिए विफल रहता है:> Could not get unknown property 'referencedString'
jpage4500

यह aapt2 के साथ टूटता है
Snicolas

2
मैंने एक प्लगइन बनाया है जो बहुत कुछ वैसा ही करता है और aapt2 के साथ काम करता है: github.com/LikeTheSalad/android-string-reference :)
César Muñoz

7

आप अपने स्वयं के तर्क का उपयोग कर सकते हैं, जो नेस्टेड स्ट्रिंग्स को पुनरावर्ती रूप से हल करता है।

/**
 * Regex that matches a resource string such as <code>@string/a-b_c1</code>.
 */
private static final String REGEX_RESOURCE_STRING = "@string/([A-Za-z0-9-_]*)";

/** Name of the resource type "string" as in <code>@string/...</code> */
private static final String DEF_TYPE_STRING = "string";

/**
 * Recursively replaces resources such as <code>@string/abc</code> with
 * their localized values from the app's resource strings (e.g.
 * <code>strings.xml</code>) within a <code>source</code> string.
 * 
 * Also works recursively, that is, when a resource contains another
 * resource that contains another resource, etc.
 * 
 * @param source
 * @return <code>source</code> with replaced resources (if they exist)
 */
public static String replaceResourceStrings(Context context, String source) {
    // Recursively resolve strings
    Pattern p = Pattern.compile(REGEX_RESOURCE_STRING);
    Matcher m = p.matcher(source);
    StringBuffer sb = new StringBuffer();
    while (m.find()) {
        String stringFromResources = getStringByName(context, m.group(1));
        if (stringFromResources == null) {
            Log.w(Constants.LOG,
                    "No String resource found for ID \"" + m.group(1)
                            + "\" while inserting resources");
            /*
             * No need to try to load from defaults, android is trying that
             * for us. If we're here, the resource does not exist. Just
             * return its ID.
             */
            stringFromResources = m.group(1);
        }
        m.appendReplacement(sb, // Recurse
                replaceResourceStrings(context, stringFromResources));
    }
    m.appendTail(sb);
    return sb.toString();
}

/**
 * Returns the string value of a string resource (e.g. defined in
 * <code>values.xml</code>).
 * 
 * @param name
 * @return the value of the string resource or <code>null</code> if no
 *         resource found for id
 */
public static String getStringByName(Context context, String name) {
    int resourceId = getResourceId(context, DEF_TYPE_STRING, name);
    if (resourceId != 0) {
        return context.getString(resourceId);
    } else {
        return null;
    }
}

/**
 * Finds the numeric id of a string resource (e.g. defined in
 * <code>values.xml</code>).
 * 
 * @param defType
 *            Optional default resource type to find, if "type/" is not
 *            included in the name. Can be null to require an explicit type.
 * 
 * @param name
 *            the name of the desired resource
 * @return the associated resource identifier. Returns 0 if no such resource
 *         was found. (0 is not a valid resource ID.)
 */
private static int getResourceId(Context context, String defType,
        String name) {
    return context.getResources().getIdentifier(name, defType,
            context.getPackageName());
}

एक से Activity, उदाहरण के लिए, यह बहुत पसंद है फोन

replaceResourceStrings(this, getString(R.string.message_text));

2

मुझे पता है कि यह एक पुरानी पोस्ट है, लेकिन मैं त्वरित 'एन गंदे समाधान को साझा करना चाहता था जिसे मैं अपनी परियोजना के लिए लेकर आया हूं। यह केवल TextViews के लिए काम करता है, लेकिन अन्य विजेट के लिए भी अनुकूलित किया जा सकता है। ध्यान दें कि इसके लिए वर्ग कोष्ठक (जैसे [@string/foo]) में संलग्न होना आवश्यक है ।

public class RefResolvingTextView extends TextView
{
    // ...

    @Override
    public void setText(CharSequence text, BufferType type)
    {
        final StringBuilder sb = new StringBuilder(text);
        final String defPackage = getContext().getApplicationContext().
                getPackageName();

        int beg;

        while((beg = sb.indexOf("[@string/")) != -1)
        {
            int end = sb.indexOf("]", beg);
            String name = sb.substring(beg + 2, end);
            int resId = getResources().getIdentifier(name, null, defPackage);
            if(resId == 0)
            {
                throw new IllegalArgumentException(
                        "Failed to resolve link to @" + name);
            }

            sb.replace(beg, end + 1, getContext().getString(resId));
        }

        super.setText(sb, type);
    }
}

इस दृष्टिकोण का नकारात्मक पक्ष यह है कि एक में setText()परिवर्तित हो CharSequenceजाता है String, जो एक मुद्दा है अगर आप चीजों को पास करते हैं जैसे SpannableString; मेरे प्रोजेक्ट के लिए यह कोई समस्या नहीं थी क्योंकि मैंने केवल इसका उपयोग किया था, इसके लिए TextViewsमुझे अपने एक्सेस की आवश्यकता नहीं थी Activity


इस सवाल पर एक जवाब के लिए यह निकटतम चीज है। मुझे लगता है कि हमें लेआउट xml पार्सिंग में हुक करने की आवश्यकता है।
एरिक वुड्रूफ़

2

नए डेटा बाइंडिंग के साथ आप अपने xml में अधिक सम्‍मिलित और कर सकते हैं।

उदाहरण के लिए यदि आपको संदेश 1 और संदेश 2 मिला है, तो आप यह कर सकते हैं:

android:text="@{@string/message1 + ': ' + @string/message2}"

तुम भी कुछ पाठ बर्तन आयात और String.format और दोस्तों को फोन कर सकते हैं।

दुर्भाग्य से अगर आप इसे कई जगहों पर फिर से उपयोग करना चाहते हैं तो यह गड़बड़ हो सकता है, आप हर जगह इस कोड टुकड़े को नहीं चाहते हैं। और आप उन्हें एक जगह पर xml में परिभाषित नहीं कर सकते (ऐसा नहीं है कि मुझे पता है) ताकि आप एक ऐसा वर्ग बना सकें जो उन रचनाओं को संक्षिप्त कर देगा:

public final class StringCompositions {
    public static final String completeMessage = getString(R.string.message1) + ": " + getString(R.string.message2);
}

तब आप इसके बजाय इसका उपयोग कर सकते हैं (आपको डेटा बाइंडिंग के साथ वर्ग आयात करने की आवश्यकता होगी)

android:text="@{StringCompositions.completeMessage}"

2

फ्रांसेस्को लौरिता https://stackoverflow.com/a/39870268/9400836 द्वारा उपरोक्त उत्तर के अलावा

ऐसा लगता है कि एक संकलित त्रुटि है "& निकाय; संदर्भित किया गया था, लेकिन घोषित नहीं किया गया" जो इसके लिए बाहरी उल्लेख को संदर्भित करके हल किया जा सकता है

रेस / कच्चे / entities.ent

<!ENTITY appname "My App Name">

res / values ​​/ strings.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE resources [
    <!ENTITY appname SYSTEM "/raw/entities.ent">
]>
<resources>
    <string name="app_name">&appname;</string>
</resources

हालांकि यह संकलित करता है और चलाता है इसका एक खाली मूल्य है। शायद किसी को पता है कि यह कैसे हल करना है। मैंने एक टिप्पणी पोस्ट की होगी, लेकिन न्यूनतम प्रतिष्ठा 50 है।


1
अब आपके पास 53 है।
कूलमाइंड

1

आप स्ट्रिंग प्लेसहोल्डर (% s) का उपयोग कर सकते हैं और रन-टाइम पर जावा का उपयोग करके उन्हें बदल सकते हैं

<resources>
<string name="button_text">Add item</string>
<string name="message_text">Custom text %s </string>
</resources>

और जावा में

String final = String.format(getString(R.string.message_text),getString(R.string.button_text));

और फिर इसे उस जगह पर सेट करें जहां यह स्ट्रिंग का उपयोग करता है


आप अन्य समाधानों की प्रतिलिपि बनाएँ। कई तार, उपयोग संदर्भ जब %1$s, %2$sआदि के बजाय %s
कूलमैन्ड

@CoolMind आप कॉपी के संदर्भ का उल्लेख कर सकते हैं।
इस्माइल इकबाल

1

मैंने एक छोटी सी लाइब्रेरी बनाई है जो आपको इन प्लेसहोल्डर्स को बिल्डटाइम पर हल करने की अनुमति देती है, इसलिए आपको जो भी चाहिए उसे प्राप्त करने के लिए आपको कोई जावा / कोटलिन कोड नहीं जोड़ना होगा।

अपने उदाहरण के आधार पर, आपको अपने तार इस तरह सेट करने होंगे:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="button_text">Add item</string>
    <string name="template_message_text">You don't have any items yet! Add one by pressing the ${button_text} button.</string>
</resources>

और फिर प्लगइन निम्नलिखित को उत्पन्न करने का ध्यान रखेगा:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="message_text">You don't have any items yet! Add one by pressing the Add item button.</string>
</resources>

यह स्थानीयकृत और स्वाद के तार के लिए भी काम करता है, जब भी आप अपने टेम्प्लेट या इसके मूल्यों में बदलाव करते हैं, तो उत्पन्न स्ट्रिंग्स स्वयं को अपडेट रखेंगे।

अधिक जानकारी यहाँ: https://github.com/LikeTheSalad/android-string-reference


मैंने आपका पुस्तकालय आजमाया। आपके द्वारा दिए गए चरणों का पालन करने के बाद। यह बिल्ड एरर देता रहता है। आपका पुस्तकालय सब पर काम नहीं करता है
developer1011

मैं देखता हूं, यह सुनकर दुख हुआ। यदि आप चाहें, तो कृपया यहां एक समस्या बनाएं: github.com/LikeTheSalad/android-string-reference/issues लॉग के साथ और मैं इसे asap के मामले में संबोधित कर सकता हूं कि यह एक समस्या है, या यदि यह एक कॉन्फ़िगरेशन समस्या है तो मैं भी मदद कर सकता हूं सुनो। 👍
सेसार मुनोज़
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.